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

發布日期:2025 年 9 月 22 日

啟動檢視區塊轉場效果時,瀏覽器會自動擷取標記 view-transition-name 的元素前後快照。這些快照會以虛擬元素樹狀結構呈現。根據預設,產生的樹狀結構是「扁平」的。這表示 DOM 中的原始階層會遺失,而所有擷取的檢視區塊轉換群組都會是單一 ::view-transition 虛擬元素下的同層級元素。

這種扁平樹狀結構方法足以應付許多用途,但有些樣式用途無法透過這種方法達成。以下列舉幾項效果,這些效果可能會在扁平樹狀結構中產生非預期的視覺效果:

  • 剪輯 (overflowclip-pathborder-radius):剪輯會影響元素的子項,也就是說,檢視區塊轉場效果群組的同層級項目無法互相剪輯。
  • opacitymask-imagefilter:同樣地,這些效果的設計目的是在樹狀結構的完整點陣化圖片上運作,影響子項,而不是個別影響每個項目。
  • 3D 轉換 (transform-styletransformperspective):如要顯示完整範圍的 3D 轉換動畫,必須維持部分階層。

以下範例顯示平面虛擬樹狀結構,其中的元素會遭到 DOM 樹狀結構中祖先的剪裁。這些元素會在檢視區塊轉場期間失去裁剪效果,導致視覺效果中斷。

檢視畫面轉換期間,錄製的剪輯效果中斷。對話方塊應剪裁文字,但實際並未剪裁。動畫時間會放慢,以誇大效果。

巢狀檢視區塊轉換群組是檢視區塊轉換的擴充功能,可讓您在彼此之間巢狀化 ::view-transition-group 虛擬元素。如果檢視區塊轉場效果群組是巢狀結構,轉場期間可能會還原裁剪等效果。

Browser Support

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

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

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

現場示範

示範錄音

示範錄音 (慢速)

仔細觀察示範,您會發現轉場效果有問題:即使說明段落是 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) 虛擬元素剪裁段落,請將 overflow: clip 套用至 ::view-transition-group-children(card) 虛擬元素:

::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) 虛擬元素中,並由該元素裁剪。#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 樹狀結構的拓撲。這項功能可解鎖各種效果,是先前檢視區塊轉場效果無法實現的,我們已在此處說明部分效果。

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