테두리 광을 회전시키고 three.js 모바일 대응을 고친다
회전 테두리
conic 의 각도를 @property 로 돌리면 매 프레임 배경을 다시 칠한다 —
컴포지터에서 못 돌고 메인 스레드가 페인트한다(하드 게이트 8).
정사각형 원뿔 레이어를 마스크 안에서 rotate 시켰다. transform 만 움직인다.
aspect-ratio: 1 이라야 회전 중 밝기가 일정하고,
width: 200% 라야 1178x52 헤더에서 모서리까지 닿는다. 주기 11초.
three.js 모바일
세로 화면에서 게이트가 통째로 안 보이고 있었다.
수직 시야는 화면 비율과 무관하지만 수평 시야는 aspect 에 비례한다.
데스크톱(2.5:1)에서 정한 판 폭 15 가 세로 화면(0.45:1)의 수평 시야
2.5 에 비해 여섯 배라, 첫 판이 화면을 완전히 덮었다.
fit() 에서 camera.aspect 를 갱신할 때 판의 scale.x 도 같이 잡는다.
저사양 판정도 고쳤다. hardwareConcurrency <= 4 만 보면 요즘 폰이 전부
고사양으로 잡힌다(8코어가 흔하다). innerWidth < 760 을 함께 본다.
DPR 1.5 -> 1.25, 캔버스 불투명도 0.7 -> 0.45(세로는 화면 전체를 덮는다).
스킬
svg-filters.md 테두리 광 회전은 각도가 아니라 정사각형 레이어 rotate
three.md 지오메트리를 화면 비율에 맞춰 다시 잡아라
저사양 판정에 화면 폭을 넣어라
This commit is contained in:
parent
54ec6fa582
commit
8267905c0c
8 changed files with 171 additions and 3 deletions
|
|
@ -402,3 +402,35 @@ if (reduceEffects()) document.documentElement.classList.add('reduce-effects');
|
|||
글로우를 더한다면 테두리에서 얕게 번지는 정도로 충분하다 —
|
||||
`0 0 22px -8px` 처럼 **음수 spread** 를 써서 형태 밖으로 새지 않게 한다.
|
||||
밝은 배경에서는 흰빛 대신 그림자로 뒤집어야 같은 인상이 난다.
|
||||
|
||||
### 테두리 광을 회전시키려면
|
||||
|
||||
`conic-gradient(from var(--angle), …)` 를 `@property` 로 애니메이션하는 방법이 흔히 보이는데,
|
||||
**그건 매 프레임 배경을 다시 칠한다.** 컴포지터에서 못 돌고 메인 스레드가 페인트한다.
|
||||
`transform`·`opacity` 만 애니메이션하라는 규칙에 걸린다.
|
||||
|
||||
**정사각형 원뿔 레이어를 마스크 안에서 회전시켜라.** 움직이는 것은 `rotate` 뿐이다.
|
||||
|
||||
```css
|
||||
.rim { /* 테두리만 남기는 마스크 */
|
||||
position: absolute; inset: 0; border-radius: inherit;
|
||||
overflow: hidden; padding: 1px;
|
||||
mask: linear-gradient(black 0 0) content-box, linear-gradient(black 0 0);
|
||||
mask-composite: exclude;
|
||||
}
|
||||
.rim::before { /* 실제로 도는 것 */
|
||||
content: ""; position: absolute; top: 50%; left: 50%;
|
||||
width: 200%; aspect-ratio: 1; /* 정사각형이라 회전해도 밝기가 균일하다 */
|
||||
translate: -50% -50%;
|
||||
background: conic-gradient(from 0deg, transparent 0 58%, var(--glow) 74%, transparent 86%);
|
||||
animation: rim-spin 11s linear infinite;
|
||||
}
|
||||
@keyframes rim-spin { to { rotate: 360deg; } }
|
||||
```
|
||||
|
||||
**두 가지를 지켜라.**
|
||||
|
||||
- `aspect-ratio: 1` — 정사각형이어야 회전 중 밝기가 일정하다. 납작하면 변마다 달라진다
|
||||
- `width: 200%` — 요소보다 크게 잡아야 납작한 형태에서도 모서리까지 닿는다
|
||||
|
||||
`prefers-reduced-motion` 에서는 애니메이션만 빼면 된다. 정지 상태의 광은 그대로 남는다.
|
||||
|
|
|
|||
|
|
@ -75,6 +75,47 @@ h1 이 36px 이어도 히어로가 압도적이다.
|
|||
**파일 이름을 용도와 맞춰라.** `hero-scene.ts` 를 파이프라인에서 쓰기 시작한 순간
|
||||
다음 사람은 그것을 히어로 것인 줄 알고 고친다. `gate-scene.ts` 로 바꿔라.
|
||||
|
||||
### 지오메트리는 화면 비율에 맞춰 다시 잡아라
|
||||
|
||||
데스크톱에서 정한 크기를 세로 화면에 그대로 쓰면 씬이 통째로 안 보인다.
|
||||
|
||||
수직 시야는 화면 비율과 무관하지만 **수평 시야는 aspect 에 비례한다.**
|
||||
|
||||
```
|
||||
수직 시야 = 2 · d · tan(fov/2)
|
||||
수평 시야 = 수직 시야 × aspect
|
||||
```
|
||||
|
||||
실측 사례: 판 폭 15 를 데스크톱(2.5:1)에서 정했다. 세로 화면(0.45:1)에서 수평 시야는
|
||||
**2.5** 로 줄어드는데 판은 그대로 15 라 **여섯 배**가 됐다. 첫 판이 화면을 완전히 덮어
|
||||
게이트가 보이지 않고 그냥 밝은 화면이 됐다.
|
||||
|
||||
```js
|
||||
const fitPanes = (aspect) => {
|
||||
const halfH = Math.tan((camera.fov * Math.PI) / 360);
|
||||
const viewW = 2 * camDist * halfH * aspect;
|
||||
const want = Math.max(MIN_W, viewW * 1.12); // 살짝만 덮는다
|
||||
for (const m of panes) m.scale.x = Math.min(1, want / GEO_W);
|
||||
};
|
||||
```
|
||||
|
||||
`fit()` 안에서 `camera.aspect` 를 갱신할 때 **같이 부른다.**
|
||||
|
||||
### 저사양 판정에 화면 폭을 넣어라
|
||||
|
||||
```js
|
||||
const narrow = innerWidth < 760;
|
||||
const lean = narrow || (navigator.hardwareConcurrency ?? 4) <= 4;
|
||||
const dpr = Math.min(devicePixelRatio, narrow ? 1.25 : 1.5);
|
||||
```
|
||||
|
||||
**`hardwareConcurrency` 만 보면 요즘 폰이 전부 고사양으로 잡힌다** — 8코어가 흔하다.
|
||||
좁은 화면은 대개 배터리로 돌아가고, 같은 씬이라도 열이 더 난다.
|
||||
폰의 DPR 은 3 까지 가므로 좁은 화면에서는 한 단계 더 내린다.
|
||||
|
||||
**세로 화면에서 캔버스가 화면 전체를 덮는다면 불투명도도 낮춰라.**
|
||||
가로에서 적당하던 밝기가 세로에서는 본문을 묻는다. 실측 교정: `0.7 → 0.45`.
|
||||
|
||||
### WebGL 위의 대비는 `readPixels` 로 못 잰다
|
||||
|
||||
`preserveDrawingBuffer: false`(기본값)면 프레임을 그린 뒤 버퍼가 비워진다.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue