From af3a08397e9f67c3e69733f165739077b7581d78 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Thu, 20 Aug 2026 16:08:11 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B0=B0=EA=B2=BD=EC=9D=84=20=EB=B7=B0?= =?UTF-8?q?=ED=8F=AC=ED=8A=B8=EC=97=90=20=EA=B3=A0=EC=A0=95=ED=95=98?= =?UTF-8?q?=EA=B3=A0,=20=EA=B2=8C=EC=9D=B4=ED=8A=B8=20=EC=94=AC=EC=9D=84?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC=EC=9D=B8=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=98=AE=EA=B8=B4=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "스크롤할 때 이미지가 고정되어 원경처럼 보이는 게 패럴랙스의 꽃"이라는 지적이 맞았다. 느리게 같이 움직이는 것으로는 그 인상이 안 나온다 — 속도만 다를 뿐 방향이 같기 때문이다. 고정 배경 background-attachment: fixed 는 iOS Safari 가 GPU 메모리 때문에 throttle 해서 스크롤 중 배경이 튄다. 쓰지 않았다. 섹션에 clip-path: inset(0) 을 걸어 position: fixed 의 컨테이닝 블록으로 만들었다. 배경이 화면에 멈춘 채 그 섹션 안에서만 보인다. 실측: 스크롤 1000px 구간에서 viewportTop 이 계속 0. 대가 둘을 받아들였다 조상에 transform/filter/mask 를 못 쓴다 — 페이드 마스크를 포기했다 prefers-reduced-motion 에서는 absolute 로 되돌린다 전부 고정하지는 않았다. 종이는 손에 닿는 거리라 원경으로 두면 거짓말이다 재료·설치·프리플라이트 fixed 원경 사례 drift 두 결과물이 흐르는 자리 조판 drift 종이는 가깝다 게이트 씬을 파이프라인으로 히어로에서는 의미를 판독기가 자막으로 설명해야 했다. 그림이 스스로 말하지 못한다는 뜻이다. 옆에 여섯 단계가 적힌 자리로 옮기니 자막이 필요 없어졌다 — 판을 통과하면 그 단계가 켜진다. 캔버스는 sticky, 진행도는 섹션에서 읽는다. travel 을 섹션 높이로 잡았더니 캔버스가 사라진 뒤에도 진행이 남아 6단계 중 3단계에서 멈췄다. (섹션 높이 - 뷰포트 높이)로 고쳤다. 캔버스 위 텍스트 대비 14.2:1. 스킬 motion.md 패럴랙스의 원형은 고정 · clip-path 기법과 그 대가 sticky 캔버스의 진행도 계산 three.md 씬의 의미는 옆의 콘텐츠가 반응할 때 성립한다. 자막이 필요하면 배치를 의심해라 --- apps/site/design.md | 41 ++++++++++ apps/site/src/components/BgPhoto.astro | 93 +++++++++++++---------- apps/site/src/components/Install.astro | 2 +- apps/site/src/components/Materials.astro | 2 +- apps/site/src/components/Pipeline.astro | 77 ++++++++++++++++++- apps/site/src/components/Preflight.astro | 2 +- apps/site/src/components/Showcase.astro | 2 +- apps/site/src/components/Typography.astro | 2 +- apps/site/src/lib/hero-scene.ts | 21 +++-- apps/site/src/styles/base.css | 16 +++- packages/skill/references/motion.md | 51 +++++++++++++ packages/skill/references/three.md | 28 +++++++ 12 files changed, 281 insertions(+), 56 deletions(-) diff --git a/apps/site/design.md b/apps/site/design.md index 83e4f4c..6219ff6 100644 --- a/apps/site/design.md +++ b/apps/site/design.md @@ -221,6 +221,47 @@ WebGL 게이트: `prefers-reduced-motion` 이면 **아예 시작하지 않는다 한 층은 반드시 고정이다 — 파이프라인의 단계명은 움직이지 않는다. 전부 움직이면 기준이 없어 그냥 흔들리는 페이지가 된다. +### 패럴랙스를 "고정"으로 바꿨다 + +세 번 고친 뒤에도 티가 나지 않았다. 원인은 **방식 자체**였다 — +느리게 같이 움직이는 것으로는 원경이 안 된다. 속도만 다를 뿐 방향이 같기 때문이다. + +**배경을 뷰포트에 고정한다.** `background-attachment: fixed` 는 iOS Safari 가 throttle 하므로 +쓰지 않고, 섹션에 `clip-path: inset(0)` 을 걸어 **`position: fixed` 의 컨테이닝 블록**으로 만든다. +그러면 배경이 화면에 멈춘 채 그 섹션 안에서만 보인다. + +실측: 스크롤 1000px 구간에서 배경의 `viewportTop` 이 계속 **0**. + +대가가 둘 있고 둘 다 받아들였다. + +- **조상에 `transform`·`filter`·`mask` 를 못 쓴다.** 그것들도 컨테이닝 블록을 만들어 + 고정을 깨뜨린다. 그래서 위아래 페이드 마스크를 포기하고 경계는 clip 으로 딱 자른다 +- `prefers-reduced-motion` 에서는 `position: absolute` 로 되돌린다 + +**전부 고정하지는 않았다.** 원경과 중경을 나눴다. + +| 섹션 | 모드 | 왜 | +|---|---|---| +| 재료 · 설치 · 프리플라이트 | `fixed` | 원경. 콘텐츠만 그 위를 지나간다 | +| 사례 | `drift` 0.4 | 두 결과물이 흐르는 자리라 배경도 같이 흐른다 | +| 조판 | `drift` 0.25 | **종이는 손에 닿는 거리다.** 원경으로 두면 거짓말이 된다 | + +### 게이트 씬을 파이프라인으로 옮겼다 + +히어로에서 뺐던 유리판 여섯 장을 여기로 되살렸다. **같은 씬인데 자막이 필요 없어졌다.** + +| | 히어로에 뒀을 때 | 파이프라인으로 옮긴 뒤 | +|---|---|---| +| 옆에 있는 것 | 헤드라인과 CTA | **여섯 단계의 실제 이름** | +| 의미 전달 | `게이트 01 / 06` 판독기 — 자막 | 판을 통과하면 그 단계가 켜진다 | + +캔버스는 `position: sticky; height: 100dvh` 로 화면에 머물고 섹션이 지나간다. +**진행도는 섹션 높이가 아니라 (섹션 높이 − 뷰포트 높이)로 잰다.** +섹션 높이로 나눴더니 캔버스가 사라진 뒤에도 진행이 남아 6단계 중 **3단계에서 멈췄다.** +그리고 기준 요소는 캔버스가 아니라 섹션이다 — sticky 캔버스는 자기 rect 가 변하지 않는다. + +캔버스 위 텍스트 대비 실측 **14.2:1**(캔버스 불투명도 0.7). + ### 패럴랙스가 세 번 실패한 경위 **1. 이동량이 모자랐다.** 여유를 28% 잡아놓고 `±46px`(총 92px)만 움직였다. diff --git a/apps/site/src/components/BgPhoto.astro b/apps/site/src/components/BgPhoto.astro index ee6f01f..a1ee278 100644 --- a/apps/site/src/components/BgPhoto.astro +++ b/apps/site/src/components/BgPhoto.astro @@ -8,47 +8,53 @@ import align from "../assets/bg/bg-align.webp"; /** * 배경은 섹션마다 다른 말을 해야 한다. 같은 그림을 전부에 깔면 그건 벽지다. * 넷 다 gpt-image-2 로 만들었고, 프롬프트에 이 페이지의 토큰 값을 그대로 넣었다. - * - * refraction 겹친 유리판을 통과한 빛 재질이 주제인 자리 - * split 한 빔이 둘로 갈라진다 같은 절차, 다른 결과 - * align 정렬선 하나가 어긋나 있다 자기 검사 - * paper 무광 인쇄용지 밝은 밴드. 한글이 종이 위에 있어야 한다 */ const SOURCES = { refraction, paper, split, align } as const; interface Props { variant?: keyof typeof SOURCES; /** - * 패럴랙스 강도. 이미지를 컨테이너보다 이 비율만큼 크게 만들고, - * 스크롤 구간 동안 정확히 그 초과분만큼 움직인다. - * 0.45 면 섹션 높이가 2000px 일 때 900px 을 이동한다. + * `fixed` — 뷰포트에 고정. 콘텐츠만 그 위를 지나간다. 무한히 먼 원경. + * `drift` — 스크롤보다 느리게 따라온다. 중경. 종이처럼 가까운 재질에 쓴다. */ + mode?: "fixed" | "drift"; + /** drift 전용. 이미지를 컨테이너보다 이 비율만큼 키우고 그 초과분 전체를 움직인다 */ depth?: number; - /** 0~1 */ opacity?: number; - /** 좌우 반전. 같은 그림을 두 섹션에 쓸 때 되풀이로 안 읽히게 한다 */ flip?: boolean; } -const { variant = "refraction", depth = 0.45, opacity = 0.9, flip = false } = Astro.props; +const { + variant = "refraction", + mode = "fixed", + depth = 0.35, + opacity = 0.9, + flip = false, +} = Astro.props; const src = SOURCES[variant]; // 밝은 종이만 그대로 얹고, 어두운 광원 사진은 더하기 합성한다. const additive = variant !== "paper"; -// 이미지 높이 = (1 + depth) 배. 이동량은 이미지 자기 높이 기준 비율로 환산한다. -// 예: depth 0.45 -> 높이 145%, 이동 -(0.45 / 1.45) = -31.03% const scale = 1 + depth; const shift = -(depth / scale) * 100; --- {/* - - `loading="lazy"` — 초기 번들에 들어가지 않는다. CSS background-image 로 깔면 - 이 통제권을 잃는다(항상 받고, 최적화도 안 된다). 그래서 를 절대배치한다 - - `aria-hidden` + 빈 alt — 장식이다. 보조기술이 읽을 것이 없다 + 패럴랙스의 원형은 "배경이 고정되어 원경처럼 보이는 것"이다. + 느리게 같이 움직이는 것으로는 그 인상이 안 나온다 — 속도만 다를 뿐 같은 방향이다. + + `background-attachment: fixed` 는 쓰지 않는다. iOS Safari 가 GPU 메모리 때문에 + 그것을 throttle 해서 스크롤 중 배경이 튀거나 리셋된다. + 대신 부모에 `clip-path: inset(0)` 을 걸어 **fixed 의 컨테이닝 블록**으로 만든다. + 그러면 배경은 뷰포트에 고정되면서 그 섹션 영역에서만 보인다. + + 주의: 조상에 `transform`·`filter`·`mask` 가 있으면 그게 컨테이닝 블록이 되어 + 이 기법이 통째로 깨진다. 그래서 위아래 페이드 마스크를 포기했다 — + 마스크를 걸면 고정이 사라진다. 경계는 clip 으로 딱 자른다. */}