왜 나의 3D View Transition이 작동하지 않을까?
요약
CSS View Transition API를 사용할 때 3D 애니메이션이 제대로 작동하지 않는 원인과 해결 방법을 다룹니다. 부모 요소의 perspective 설정과 HTML 구조의 중요성을 설명하며, 이미지와 같은 교체 요소에서의 3D 전환 구현 방식을 안내합니다.
핵심 포인트
- 3D 전환을 위해서는 부모 컨테이너에 perspective 속성 설정이 필수적임
- View Transition 스냅샷은 요소의 평면화(flattening) 특성을 가짐
- HTML 구조 설계 시 컨테이너와 자식 요소 간의 관계가 3D 효과에 영향을 미침
- @view-transition 규칙을 통해 문서 간 뷰 전환 활성화 가능
View Transition을 많이 다뤄보셨다면, 두 페이지 간의 3D 전환(즉, cross-document view transitions)이 제대로 작동하지 않는 것처럼 보인다는 점을 눈치채셨을 수도 있습니다. 즉, 최소한 브라우저가 먼저 요소들을 평면화(flattening)하지 않는다면 말이죠.
이미지 요소는 이를 증명하기 위한 가장 좋은 예시입니다. View Transition에서 브라우저가 전후 상태를 찍는 스냅샷(snapshots)처럼, 이미지도 교체 요소 (replaced elements)이기 때문에, 이론적으로는 3D 애니메이션을 위한 일종의 축소된 테스트 케이스로 사용할 수 있어야 하기 때문입니다. 예를 들어, 클릭 시 한 이미지를 뒤집어 다른 이미지를 보여주는 모습은 다음과 같습니다:
CodePen Embed Fallback
애니메이션이 제대로 작동하려면 이미지의 부모 컨테이너(이 경우 .scene 요소)에 perspective 속성을 설정해야 한다는 점이 중요합니다. 그렇지 않으면 3D 변형(transformation)은 그저 평면적일 뿐입니다. 이는 요소의 외형에 일종의 _각도(angles)_를 부여하는 것과 같습니다:
CodePen Embed Fallback
CSS에서 부모의 perspective는 자기 자신을 제외한 모든 자식 요소에 적용됩니다:
.scene {
perspective: 1200px;
...
여기서 중요한 것은 HTML 구조입니다. 구체적으로 .scene 컨테이너가 자식인 .card 요소들 위에 놓여 3D 효과가 살아나게 함으로써, 뒤집기(flip)가 의도한 대로 보이게 만드는 방식입니다:
<div class="scene">
<div class="card">
<!-- Card Content Here -->
...
아마도 .cards를 뒤집기 위한 우리의 키프레임 애니메이션(keyframe animation)은 다음과 같을 것입니다:
@keyframes flipOut {
from {
transform: rotateY(0deg);
...
그리고 이를 .cards에 다음과 같이 적용합니다:
.card.flip-out {
animation: flipOut 5.2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
...
….card 요소에 .flip-out 클래스가 추가될 때(클릭을 감지하는 JavaScript 덕분에) 애니메이션이 forwards로 실행되고, .flip-in 클래스가 추가될 때 reverse로 실행되는 곳입니다.
이것이 문서 간 뷰 전환 (cross-document view transition)이 작동해야 하는 방식이기도 하죠, 그렇지 않나요? 만약 이미지가 3D 애니메이션을 지원한다면, 뷰 전환 스냅샷 (view transition snapshot) 또한 동일하게 작동해야 합니다. 이제 이를 자세히 파헤쳐 봅시다.
뷰 전환 (view transition) 설정하기
가장 먼저 해야 할 일은 [@view-transition](https://css-tricks.com/almanac/rules/v/view-transition/) at-rule을 사용하여 두 페이지 모두에서 navigation 디스크립터 (descriptor)를 auto로 설정함으로써 뷰 전환을 사용하도록 선택하는 것입니다.
@view-transition {
navigation: auto;
}
다른 설정을 아무것도 하지 않는다면, 두 페이지 사이를 탐색할 때 한 페이지가 다른 페이지로 페이드 인/아웃 (fade) 됩니다. 이는 모든 문서 간 뷰 전환 중 가장 기본적인 형태입니다.
그렇다면 어떻게 커스텀할 수 있을까요? 우리는 ::view-transition-old()와 ::view-transition-new() 의사 클래스 (pseudo-classes)를 사용합니다. 여기서 전자는 "이전" 스냅샷이고 후자는 "새로운" 스냅샷입니다. 지난 예제에서 사용했던 .card 요소와 마찬가지로, 이곳에 키프레임 애니메이션 (keyframe animation)을 설정합니다.
::view-transition-old(root) {
/* 애니메이션이 여기에 들어갑니다 */
}
...
root 파라미터는 뷰 전환이 페이지 전체와 뷰 전환의 기본 스냅샷 그룹에 의해 생성된(및 생성되지 않은) 모든 요소를 대상으로 하도록 지시합니다.
여기에 문제가 있습니다
view transitions가 웹페이지 전체의 스냅샷을 생성하기 때문에, (논리적으로) <html> 요소(또는 :root)가 대상이 되어야 한다고 생각할 수 있습니다. 맞죠? 제 말은, 뷰 전환(view transition)이 존재할 때 DOM 트리는 다음과 같은 모습이기 때문입니다:
html
├─ ::view-transition
│ ├─ ::view-transition-group(card)
...
따라서 전체 스냅샷이 perspective를 적용해야 하는 위치여야 합니다. 그렇죠? 하지만 결과는 그렇지 않았습니다.
사실, 아무런 효과도 나타나지 않습니다! 이전에 카드에서 사용할 수 있었던 아름다운 3D 플립(3D flip) 대신 다음과 같은 결과만 남게 됩니다:
제가 작업하던 코드는 다음과 같습니다:
/* Cross-document View Transition opt-in */
@view-transition {
navigation: auto;
...
참고: 여기서는 애니메이션을 반전시키지 않았습니다. -90deg로 플립한 다음 90deg에서 시작하기 때문입니다. 완전히 같지는 않습니다!
그리고 perspective가 html에 있든 :root에 있든 상관없이 작동하지 않습니다:
/* 👎 */
html {
perspective: 1100px;
...
조금 더 조사해 본 결과, perspective(및 일반적인 3D 변형 (3D transformations))는 특이한 효과를 유발하는 몇 가지 CSS 속성 중 하나라는 것을 발견했습니다. (답은 Bramus에게 맡기죠!)
그렇다면... 어떻게 해야 할까요? 몇 가지 아이디어가 떠올랐지만, 아쉽게도 모두 실패했습니다:
body에perspective속성을 설정해 보았습니다.::view-transition-group(root)내부에perspective를 설정해 보았습니다.::view-transition의사 요소(pseudo) 내부에perspective를 설정해 보았습니다.
사실 이 문제에 대한 매우 간단한 해결책이 있습니다. 제가 이걸 알아내는 데 왜 이렇게 오래 걸렸는지 믿기지 않을 정도입니다. 바로 perspective를 전혀 사용하지 않는 것입니다!
해결 방법
요약하자면: perspective 속성 (property) 대신 [perspective()](https://css-tricks.com/almanac/functions/p/perspective) 함수를 사용해야 합니다. 그리고 예상과는 달리 ::view-transition-* 의사 요소 (pseudo) 내부가 아니라, @keyframes 애니메이션 내부에 작성해야 합니다.
@keyframes flip-out {
0% {
transform: perspective(1100px) rotateY(0deg);
...
이 단순하지만 큰 변화가 장면을 평범한 상태에서 아름다운 상태로 바꿔 놓습니다.
그 이유는 다음과 같습니다. 뷰 전환 (view transition) 의사 요소 (pseudo-element) 트리는 일반적인 HTML 흐름
_외부 (outside)_에서 렌더링됩니다. 더 구체적으로 말하면, 전체 뷰 전환 트리는 DOM 위에 자체적인 레이어로 렌더링됩니다. 하지만 특히 ::view-transition의 경우 왜 그런지는 확실하지 않지만, 제 추측으로는 각 뷰 전환 그룹 (view transition group)의 위치와 변형 (transform) 값이 브라우저에 의해 자동으로 덮어씌워지기 때문인 것 같습니다. 이로 인해 perspective 설정이 방해를 받게 됩니다.
perspective와 perspective()의 차이점은 무엇일까요? perspective 속성은 부모 요소에 적용되는 반면, perspective()는 요소 자체에 직접 적용되는 transform 속성 함수입니다. 뷰 전환 의사 트리는 진정한 부모를 가지고 있지 않기 때문에, 부모를 필요로 하지 않는 perspective()를 사용해야만 합니다. 휴.
요약하자면…
html, :root, 또는 View Transition 의 의사 클래스 (pseudo-class) 중 어느 곳에든 perspective를 설정하는 것은 작동하지 않을 것입니다. 그리고 만약 저처럼 해결책을 찾기 위해 고군분투해 오셨다면, 이 작지만 큰 perspective() 변경이 여러분이 이 문제에 직면했을 때 해결해 줄 수 있을 것이라 생각합니다. 제 말을 믿으세요. 저는 이 문제와 몇 주 동안 싸워왔고, 오늘 이 글을 쓰기 위해 다시 돌아와서야 해결책을 발견했습니다. 글쓰기의 장점이죠!
AI 자동 생성 콘텐츠
본 콘텐츠는 CSS-Tricks의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기