패럴랙스를 실제로 보이게 고치고 격자를 걷어낸다

"티가 안 난다"는 지적이 맞았다. 세 가지를 동시에 잘못하고 있었다.

1. 이동량이 모자랐다
   여유를 28% 잡아놓고 92px 만 움직였다. 스크롤 1800px 구간에서 5%다.
   공식은 이미지를 컨테이너보다 크게 만들고 초과분 전체를 움직이는 것.
   depth 0.45 -> 높이 145%, translateY(-31.03%). 같은 구간에서 374px.
   translateY(%) 는 자기 크기 기준이라 컨테이너 높이를 몰라도 맞는다.

2. overflow: hidden 이 view() 타임라인을 죽이고 있었다
   overflow: hidden 은 스크롤 컨테이너를 만들고, 그러면 자식의 view() 가
   문서가 아니라 그 상자를 기준으로 잡힌다. 이미지는 상자 안에서 늘 같은
   자리라 진행도가 고정된다. 콘솔 에러도 없고 computed 값도 정상이라
   두 스크롤 위치에서 transform 을 재보고서야 이동 0px 인 것을 알았다.
   잘라내는 일은 섹션의 overflow: clip 에 맡겼다.

3. opacity 로 눌러 이미지를 지웠다
   어두운 광원 사진을 opacity 0.5 로 깔면 검정도 빛도 같이 죽는다.
   mix-blend-mode: screen 으로 바꿨다 — 검정은 투명, 밝은 곳만 더해진다.
   그래서 불투명도 0.85~1.0 을 써도 본문 대비가 안전하다.
   생성 프롬프트도 같은 함정이었다. "어둡게"를 너무 강하게 넣어서
   안전한 이미지가 아니라 없는 이미지를 만들었다.

격자 배경 제거
   사진이 들어온 뒤로 배경이 두 겹이라 서로 경쟁했고 표로 읽혔다.
   배경도 한 섹션에 하나다.

스킬 motion.md 에 넷 다 반영
   이동량 공식 · overflow 함정 · screen 합성 · 배경은 한 겹
This commit is contained in:
Yun Chan 2026-08-20 15:57:40 +09:00
parent 5a18806fb6
commit 1ed3c95542
11 changed files with 181 additions and 85 deletions

View file

@ -448,6 +448,81 @@ JS 쪽 판정도 한 곳에 모은다. 설정이 도중에 바뀌면 반영한
안 움직이는 섹션이 하나도 없으면 움직임에 의미가 없다는 뜻이고,
`design.md` 에 "여기는 의도적으로 정지"라고 적어두지 않으면 다음 사람이 "빠뜨린 것"으로 보고 채운다.
### 이동량은 절대값이 아니라 컨테이너 대비 비율이다
**패럴랙스가 "안 보인다"의 대부분은 이동량이 모자란 것이다.**
공식은 하나다. **배경을 컨테이너보다 크게 만들고, 그 초과분 전체를 움직인다.**
```
이미지 높이 = 컨테이너 × (1 + depth)
이동량 = 컨테이너 × depth
= 이미지 높이의 depth / (1 + depth)
```
`depth: 0.45` 면 이미지는 145%, 이동은 이미지 자기 높이의 **31%**.
섹션이 2000px 이면 **900px** 을 움직인다.
```css
.bg img {
height: 145%; /* 1 + depth */
animation: bg-par linear both;
animation-timeline: view();
animation-range: cover 0% cover 100%;
}
@keyframes bg-par {
from { transform: translateY(0); }
to { transform: translateY(-31.03%); } /* depth / (1 + depth) */
}
```
`translateY(%)` 는 **자기 크기 기준**이라 컨테이너 높이를 몰라도 정확히 맞는다.
**실측 사례**: 여유를 28% 잡아놓고 `±46px`(총 92px)만 움직였더니 스크롤 1800px 구간에서
이동 비율이 5% 였다. 아무도 시차를 느끼지 못했다. 같은 구간에서 **374px** 로 올리자 보였다.
CSS-Tricks 의 예제도 `background-position: bottom 0px → bottom -400px`, 즉 400px 이다.
> 참고: `background-position` 을 애니메이션하는 방법도 있다(CSS-Tricks). 다만 그러면
> `<img loading="lazy">` 의 지연 로딩과 반응형 `srcset` 을 잃는다. 무거운 사진이면 `<img>`
> 절대배치하고 `transform` 을 쓰는 쪽이 낫다.
### `overflow: hidden` 이 view() 타임라인을 죽인다
**이것 하나에 패럴랙스가 통째로 멈춘다. 그리고 조용히 멈춘다.**
`overflow: hidden`**스크롤 컨테이너를 만든다.** 그러면 그 안에 있는 자식의
`animation-timeline: view()` 는 문서가 아니라 **그 상자**를 기준으로 잡힌다.
자식은 상자 안에서 위치가 변하지 않으므로 진행도가 고정되고, 애니메이션은
어중간한 한 프레임에서 굳는다.
- 콘솔 에러 없음. `animation-name`·`animation-timeline` computed 값도 전부 정상
- `getComputedStyle(el).transform` 을 두 스크롤 위치에서 재봐야 알 수 있다. **실측에서 이동 0px**
**잘라내야 한다면 `overflow: clip` 을 쓰거나 조상 섹션에서 처리해라.**
`clip` 은 스크롤 컨테이너를 만들지 않는다.
### 어두운 배경 위 광원 이미지는 opacity 로 누르지 마라
`opacity` 는 이미지 전체를 균일하게 죽인다 — 검정도, 빛도 같이. 그래서 어두운 광원 사진을
`opacity: 0.5` 로 깔면 **있는지조차 안 보인다.**
```css
.bg img { mix-blend-mode: screen; opacity: 0.9; }
```
`screen`**검정을 투명하게 만들고 밝은 곳만 더한다.** 그래서 불투명도를 높게 유지해도
본문 대비를 해치지 않는다. 밝은 이미지(종이·질감)는 반대로 `normal` 이 맞다.
**생성형 이미지를 배경으로 쓸 때 특히 조심해라.** 프롬프트에 "본문 뒤에 깔리니 어둡게"를
너무 강하게 넣으면 안전한 이미지가 아니라 **없는 이미지**가 나온다.
대비는 합성 방식으로 풀고, 이미지 자체는 볼 수 있게 만들어라.
### 배경도 한 섹션에 하나다
CSS 격자와 사진을 같이 깔았다가 걷어냈다. 두 배경이 서로 경쟁했고,
격자는 배경이라기보다 **표**로 읽혔다. 앞에서 "한 층은 고정"이라고 한 것과 같은 이야기다 —
층을 늘리는 것과 층이 싸우게 두는 것은 다르다.
### 패럴랙스에는 기준면이 필요하다
**배경이 단색이면 패럴랙스는 성립하지 않는다.** 앞의 것이 몇 px 어긋나 봐야