게시일: 2025년 9월 22일
뷰 전환을 시작하면 브라우저가 view-transition-name
로 태그된 요소의 전후 스냅샷을 자동으로 가져옵니다. 이러한 스냅샷은 의사 요소 트리로 렌더링됩니다. 기본적으로 생성된 트리는 '플랫'입니다. 즉, DOM의 원래 계층 구조가 손실되고 캡처된 모든 뷰 전환 그룹이 단일 ::view-transition
의사 요소 아래에 있는 형제가 됩니다.
이 플랫 트리 접근 방식은 많은 사용 사례에 충분하지만 이를 통해 달성할 수 없는 스타일 지정 사용 사례도 있습니다. 다음은 플랫 트리에서 예상치 못한 시각적 효과를 줄 수 있는 효과의 예입니다.
- 클리핑 (
overflow
,clip-path
,border-radius
): 클리핑은 요소의 하위 요소에 영향을 미치므로 뷰 전환 그룹 형제는 서로 클리핑할 수 없습니다. opacity
,mask-image
,filter
: 마찬가지로 이러한 효과는 각 항목에 개별적으로 영향을 미치지 않고 트리의 완전히 래스터화된 이미지에서 작동하도록 설계되어 하위 요소에 영향을 미칩니다.- 3D 변환 (
transform-style
,transform
,perspective
): 3D 변환 애니메이션의 전체 범위를 표시하려면 일부 계층 구조를 유지해야 합니다.
다음 예는 DOM 트리의 상위 요소에 의해 잘린 요소가 있는 플랫 의사 트리를 보여줍니다. 이러한 요소는 뷰 전환 중에 클리핑이 손실되어 시각적 효과가 깨집니다.
중첩된 뷰 전환 그룹은 뷰 전환의 확장 프로그램으로, ::view-transition-group
가상 요소를 서로 중첩할 수 있습니다. 뷰 전환 그룹이 중첩되면 전환 중에 클리핑과 같은 효과를 복원할 수 있습니다.
Browser Support
플랫 유사 트리에서 중첩된 유사 트리로
다음 데모에서 사용자의 아바타를 클릭하면 해당 사용자에 대한 자세한 정보를 확인할 수 있습니다. 애니메이션은 클릭한 버튼을 대화상자로 변환하고, 아바타와 이름을 화면 전체로 이동하고, 대화상자의 단락을 위아래로 슬라이드하는 동일한 문서 뷰 전환에 의해 처리됩니다.
실시간 데모
데모 녹화
데모 녹화 (느린 버전)
데모를 자세히 살펴보면 전환에 문제가 있음을 알 수 있습니다. 설명이 포함된 단락이 DOM의 <dialog>
요소의 하위 요소임에도 불구하고 전환 중에 텍스트가 <dialog>
상자로 클리핑되지 않습니다.
<dialog id="info_bramus" closedby="any">
<h2><img alt="…" class="avatar" height="96" width="96" src="avatar_bramus.jpg"> <span>Bramus</span></h2>
<p>Bramus is …</p>
<p>…</p>
</dialog>
<dialog>
에 overflow: clip
을 적용해도 아무 작업도 실행되지 않습니다.
문제는 뷰 전환이 의사 트리를 빌드하고 렌더링하는 방식입니다.
- 가상 트리에서 기본적으로 모든 스냅샷은 서로 형제입니다.
- 의사 트리는 전체 문서 위에 렌더링되는
::view-transition
의사 요소에 렌더링됩니다.
이 데모의 경우 DOM 트리는 다음과 같습니다.
html
├─ ::view-transition
│ ├─ ::view-transition-group(card)
│ │ └─ ::view-transition-image-pair(card)
│ │ ├─ ::view-transition-old(card)
│ │ └─ ::view-transition-new(card)
│ ├─ ::view-transition-group(name)
│ │ └─ ::view-transition-image-pair(name)
│ │ ├─ ::view-transition-old(name)
│ │ └─ ::view-transition-new(name)
│ ├─ ::view-transition-group(avatar)
│ │ └─ ::view-transition-image-pair(avatar)
│ │ ├─ ::view-transition-old(avatar)
│ │ └─ ::view-transition-new(avatar)
│ ├─ ::view-transition-group(paragraph1.text)
│ │ └─ ::view-transition-image-pair(paragraph1.text)
│ │ └─ ::view-transition-new(paragraph1.text)
│ └─ ::view-transition-group(paragraph2.text)
│ └─ ::view-transition-image-pair(paragraph2.text)
│ └─ ::view-transition-new(paragraph2.text)
├─ head
└─ body
└─ …
::view-transition-group(.text)
의사 요소는 ::view-transition-group(card)
의사 요소의 후속 형제이므로 카드 위에 그려집니다.
::view-transition-group(card)
클립 ::view-transition-group(.text)
이 되려면 ::view-transition-group(.text)
의사 요소가 ::view-transition-group(card)
의 하위 요소여야 합니다. 이를 위해 생성된 ::view-transition-group()
의사 요소에 '상위 그룹'을 할당할 수 있는 view-transition-group
를 사용합니다.
상위 그룹을 변경하는 방법에는 두 가지가 있습니다.
- 상위 요소에서
view-transition-group
을contain
로 설정하여view-transition-name
가 있는 모든 하위 요소를 포함하도록 합니다. - 모든 하위 요소에서
view-transition-group
을 상위 요소의view-transition-name
로 설정합니다.nearest
를 사용하여 가장 가까운 상위 그룹을 타겟팅할 수도 있습니다.
따라서 이 데모에서 중첩된 뷰 전환 그룹을 사용하려면 코드가 다음과 같이 됩니다.
button.clicked,
dialog {
view-transition-group: contain;
}
또는
button.clicked,
dialog *,
view-transition-group: nearest;
}
이 코드를 사용하면 ::view-transition-group(.text)
의사 요소가 이제 ::view-transition-group(card)
의사 요소 내에 중첩됩니다. 이는 모든 중첩된 의사 선택자를 함께 유지하는 추가 ::view-transition-group-children(…)
의사 선택자에서 실행됩니다.
html
├─ ::view-transition
│ ├─ ::view-transition-group(card)
│ │ ├─ ::view-transition-image-pair(card)
│ │ │ ├─ ::view-transition-old(card)
│ │ │ └─ ::view-transition-new(card)
│ │ └─::view-transition-group-children(card)
│ │ ├─ ::view-transition-group(paragraph1.text)
│ │ │ └─ ::view-transition-image-pair(paragraph1.text)
│ │ │ └─ ::view-transition-new(paragraph1.text)
│ │ └─ ::view-transition-group(paragraph2.text)
│ │ └─ ::view-transition-image-pair(paragraph2.text)
│ │ └─ ::view-transition-new(paragraph2.text)
│ ├─ ::view-transition-group(name)
│ │ └─ ::view-transition-image-pair(name)
│ │ ├─ ::view-transition-old(name)
│ │ └─ ::view-transition-new(name)
│ └─ ::view-transition-group(avatar)
│ └─ ::view-transition-image-pair(avatar)
│ ├─ ::view-transition-old(avatar)
│ └─ ::view-transition-new(avatar)
├─ head
└─ body
└─ …
마지막으로 ::view-transition-group(card)
가 단락을 의사 클립하도록 하려면 ::view-transition-group-children(card)
의사에 overflow: clip
을 적용합니다.
::view-transition-group-children(card) {
overflow: clip;
}
결과는 다음과 같습니다.
실시간 데모
데모 녹화
데모 녹화 (느린 버전)
::view-transition-group-children
의사 클래스는 중첩된 그룹이 사용될 때만 표시됩니다. 원래 요소의 border-box 크기로 지정되며, 이전 예시의 card
와 같이 가상 요소를 생성한 요소와 동일한 모양과 테두리 두께의 투명한 테두리가 지정됩니다.
클리핑 등
중첩된 뷰 전환 그룹은 클리핑 효과가 아닌 다른 곳에서 사용됩니다. 3D 효과도 그 예입니다. 다음 데모에는 전환 중에 카드를 3D로 회전하는 옵션이 있습니다.
html:active-view-transition-type(open) {
&::view-transition-old(card) {
animation-name: rotate-out;
}
&::view-transition-new(card) {
animation-name: rotate-in;
}
}
html:active-view-transition-type(close) {
&::view-transition-old(card) {
animation-name: rotate-in;
}
&::view-transition-new(card) {
animation-name: rotate-out;
}
}
중첩된 뷰 전환 그룹이 없으면 아바타와 이름이 카드와 함께 회전하지 않습니다.
실시간 데모
데모 녹화
데모 녹화 (느린 버전)
아바타와 이름 가상 요소를 카드 안에 중첩하면 3D 효과를 복원할 수 있습니다. 하지만 이것이 전부가 아닙니다. ::view-transition-old(card)
및 ::view-transition-new(card)
의사 선택자를 순환하는 것 외에도 ::view-transition-group-children(card)
의사 선택자도 순환해야 합니다.
html:active-view-transition-type(open) {
&::view-transition-group-children(card) {
animation: rotate-in var(--duration) ease;
backface-visibility: hidden;
}
}
html:active-view-transition-type(close) {
&::view-transition-group-children(card) {
animation: rotate-out var(--duration) ease;
backface-visibility: hidden;
}
}
실시간 데모
데모 녹화
데모 녹화 (느린 버전)
더 많은 데모
다음 예에서는 중첩된 뷰 전환 그룹을 사용하여 카드가 상위 스크롤러에 의해 잘리도록 합니다. 포함된 컨트롤을 사용하여 중첩된 뷰 전환 그룹의 사용을 사용 설정 또는 중지할 수 있습니다.
실시간 데모
데모 녹화
이 데모의 흥미로운 점은 모든 ::view-transition-group(.card)
의사 요소가 상위 ::view-transition-group(cards)
의사 요소 내부에 중첩되고 상위 ::view-transition-group(cards)
의사 요소에 의해 클리핑된다는 것입니다. #targeted-card
은 ::view-transition-group(cards)
에 의해 진입/종료 애니메이션이 잘리면 안 되므로 제외됩니다.
/* The .cards wrapper contains all children */
.cards {
view-transition-name: cards;
view-transition-group: contain;
}
/* Contents that bleed out get clipped */
&::view-transition-group-children(cards) {
overflow: clip;
}
/* Each card is given a v-t-name and v-t-class */
.card {
view-transition-name: match-element;
view-transition-class: card;
}
/* The targeted card is given a unique name (to style the pseudo differently)
and shouldn't be contained by the ::view-transition-group-children(cards) pseudo */
#targeted-card {
view-transition-name: targeted-card;
view-transition-group: none;
}
요약
중첩된 뷰 전환을 사용하면 의사 요소를 구성할 때 DOM 트리의 일부 토폴로지를 유지할 수 있습니다. 이를 통해 이전에는 뷰 전환으로 불가능했던 다양한 효과를 사용할 수 있으며, 그중 일부는 여기에 설명되어 있습니다.
중첩은 뷰 전환이 구성되는 모델을 변경하며 고급 효과를 만드는 데 사용됩니다. 앞서 언급한 것처럼 요소 범위 뷰 전환을 사용하면 더 간단한 모델로 효과의 일부를 구현할 수도 있습니다. 두 기능을 모두 사용해 보고 필요에 가장 적합한 기능을 결정하는 것이 좋습니다.