使用巢狀檢視區塊轉換群組,避免檢視區塊轉換發生剪裁問題 (以及其他問題)

發布日期: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

  • Chrome: 140.
  • Edge: 140.
  • Firefox: not supported.
  • Safari: not supported.

從扁平虛擬樹狀結構到巢狀虛擬樹狀結構

在下列試用版中,您可以點選使用者顯示圖片,查看該使用者的詳細資訊。動畫是由相同文件檢視畫面轉場效果處理,可將點選的按鈕變形為對話方塊、在畫面上移動顯示圖片和名稱,以及向上或向下滑動對話方塊中的段落。

現場示範

Demo 錄音

Demo 錄音 (慢速版)

如果仔細觀察示範,就會發現轉場效果有問題:雖然含有說明的段落是 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-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-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)
  ├─ head
  └─ body
        └─ …

最後,如要讓 ::view-transition-group(card) 虛擬元素裁剪段落,請將 overflow: clip 套用至 ::view-transition-group-children(card) 虛擬元素:

::view-transition-group-children(card) {
  overflow: clip;
}

結果如下:

現場示範

Demo 錄音

Demo 錄音 (慢速版)

只有在使用巢狀群組時,才會出現 ::view-transition-group-children 虛擬元素。其大小會調整為原始元素的邊框方塊,並提供透明邊框,其形狀和邊框粗細與產生虛擬元素的元素相同,即上一個範例中的 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;
    }
}

如果沒有巢狀檢視畫面轉換群組,顯示圖片和名稱就不會隨著資訊卡旋轉。

現場示範

Demo 錄音

Demo 錄音 (慢速版)

只要將顯示圖片和名稱虛擬元素嵌套在資訊卡內,就能還原 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;
    }
}

現場示範

Demo 錄音

Demo 錄音 (慢速版)

更多示範

在以下範例中,巢狀檢視轉換群組用於確保資訊卡會由其祖系捲動器裁剪。您可以使用內建控制項,切換是否使用巢狀檢視畫面轉換群組。

現場示範

Demo 錄音

這個示範的有趣之處在於,所有 ::view-transition-group(.card) 虛擬元素都會巢狀置於上層 ::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 樹狀結構的部分拓撲。這可解鎖各種先前無法透過檢視畫面轉換達成的效果,其中一些效果已在本文中說明。

巢狀結構會變更檢視畫面轉場效果的建構模式,可用於建立進階效果。如前所述,元素範圍檢視轉換也可以透過更簡單的模型,達成部分效果。建議您試用這兩項功能,找出最符合需求的選項。