問題說明
網站通常會提供物件的多個檢視畫面,例如產品清單,以及每項產品的詳細資料頁面。在兩個檢視畫面之間切換時,往往會感到不連貫。使用者點選產品縮圖查看詳細資料時,縮圖會消失,螢幕其他位置會立即顯示新的放大圖片。這種不連續性會讓使用者更難追蹤元素之間的關係。
解決方法
View Transitions API 可讓您指定在轉換前後存在於不同狀態的元素組合。在單頁導覽 (SPA) 中使用 document.startViewTransition() 觸發轉場效果時,瀏覽器會根據共用的不重複 view-transition-name 識別這些共用元素。接著,系統會自動計算位置、大小和樣式的差異,並流暢地從舊狀態轉換為新狀態。這項轉換會在頂層進行,甚至高於 z-index 值較高的元素。
導入指南
步驟 1:在 startViewTransition 中包裝狀態變更
如果是單頁應用程式 (SPA) 或簡單的狀態變更,請將更新 DOM 的邏輯包裝在 document.startViewTransition 中。瀏覽器會擷取目前狀態的快照、執行更新,然後擷取新狀態。
function navigate(view) {
// MANDATORY: Wrap the update in startViewTransition
document.startViewTransition(() => updateDOM(view));
}
步驟 2:指派共用轉場效果名稱
使用 view-transition-name CSS 屬性,告知瀏覽器應變形哪些元素。名稱可以是任何值 (none 除外)。強制規定:指定 view-transition-name 的前後最多只能有 1 個元素。如果有多個元素具有相同的 view-transition-name,DOM 會立即更新為新狀態,不會有轉場效果。
您可以使用多個 view-transition-name 變形多組元素。舉例來說,您可能想分別為產品圖片和標題設定轉場效果。
由於清單檢視畫面中有多個項目,因此無法為所有項目指定相同的 view-transition-name。在單頁應用程式中,這個問題有兩種解決方式。
動態詳細資料頁面:為清單頁面上的每個項目指派不重複的
view-transition-name,然後在選取清單項目時,將該名稱動態套用至詳細資料頁面上的相符元素,如下所示。/* In the list view, give each */ #product-1 { view-transition-name: p1 } #product-2 { view-transition-name: p2 } #product-3 { view-transition-name: p3 }function updateDOM(clickedTransitionName){ const hero = document.getElementById("hero"); hero.style.viewTransitionName = clickedTransitionName; }動態清單項目:為詳細資料頁面上的元素指派
view-transition-name,並在選取清單頁面上的項目時,將該名稱套用至該項目。返回清單頁面時,從清單頁面上的項目中移除view-transition-name。
詳細資料頁面上的 #hero 元素和清單頁面上選取的 .thumbnail 元素會共用 view-transition-name。
#hero{
view-transition-name: hero;
}
.thumbnail.selected {
view-transition-name: hero;
}
點選縮圖時,我們需要使用 .selected 類別選取器指派 view-transition-name,並在開始轉換前對 DOM 進行任何變更,藉此準備清單檢視。
接著,您可以呼叫 document.startViewTransition(),並套用變更,將頁面從詳細資料檢視畫面轉換為清單檢視畫面。
返回清單檢視畫面後,您必須清除檢視畫面轉換類別,以免下次導覽時發生錯誤。轉換的 finished 承諾解析後,即可執行這項清除作業。
// Function called when a thumbnail is clicked
function goFromListToDetail(e){
e.currentTarget.classList.add("selected");
const hero = document.getElementById("hero");
const bgColor = getComputedStyle(e.currentTarget).backgroundColor;
hero.style.background = bgColor;
// Trigger the transition, checking for support
if (!document.startViewTransition) {
document.body.classList.add("detail");
// MANDATORY Accessibility Routing: Route focus to the newly revealed heading to announce context and preserve logical tab flow
document.getElementById("detail-heading")?.focus();
return; // MANDATORY: End function execution if view transitions are not supported.
}
const transition = document.startViewTransition(() => {
document.body.classList.add("detail");
});
// MANDATORY Accessibility Routing: Route focus after the view transition resolves
transition.finished.finally(() => {
document.getElementById("detail-heading")?.focus();
});
}
// Function called when navigating from detail back to list view
function goFromDetailToList() {
if (!document.startViewTransition) {
document.body.classList.remove("detail");
document.getElementById("list-heading")?.focus();
return;
}
const transition = document.startViewTransition(() => {
document.body.classList.remove("detail");
});
// Clean up the list view and route focus
transition.finished.finally(() => {
// Route focus back to list view
document.getElementById("list-heading")?.focus();
// Remove selected classList to remove view-transition-names
document.querySelectorAll(".selected").forEach(
(element) => {
element.classList.remove("selected");
},
);
});
}
請依用途選擇合適的方法。動態清單項目需要較少的重複 CSS,但需要更多手動 JavaScript 清理。
步驟 3:修正顯示比例「拉伸」問題
根據預設,瀏覽器會在群組內淡入淡出新舊快照,並調整群組大小以容納兩者。如果您要轉換文字,請在舊檢視畫面和新檢視畫面中,將文字元素的寬度設為 fit-content,這樣轉換後的元素顯示比例就會保持穩定。
#list-page .title {
width: fit-content;
}
#detail-page #title {
width: fit-content;
}
如果轉換的元素會變更顯示比例,可能需要將舊和新虛擬元素的高度設為 ::view-transition-pair() 虛擬元素的 100%。
::view-transition-old(hero),
::view-transition-new(hero){
height: 100%;
}
虛擬元素是即時元素的快照,因此您也可以使用 object-fit 和 object-position 宣告,進一步控管轉場效果。
最佳做法
- 請勿指定過多轉場效果。只在使用者主動追蹤的主要內容 (例如主頁橫幅、標題) 使用共用元素。
- 務必在轉場效果結束後移除暫時的
view-transition-name值,以免對日後的轉場效果造成副作用。 - 請勿轉換含有有效動畫的元素。檢視畫面轉換會對快照進行操作,因此在檢視畫面轉換期間,所有動畫都會暫停。
- 務必使用
prefers-reduced-motion媒體查詢,尊重使用者減少動態效果的偏好設定。 - 強制無障礙路由:檢視畫面轉換會動態變更頁面版面配置,但不會管理程式化焦點。如果焦點仍停留在轉換期間隱藏或移除的元素上,焦點就會消失,導致鍵盤和輔助技術使用者無法瞭解上下文。在 DOM 更新後,或檢視畫面轉換的
finished承諾解析後,立即透過程式輔助方式將焦點移至更新後的頁面標題或檢視容器 (使用tabindex="-1")。
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
備用策略
View Transitions API 專為漸進增強而設計。不支援的瀏覽器則會立即執行 DOM 更新,不會顯示動畫。
function navigate(){
if (!document.startViewTransition) {
// Fallback: Just update the DOM
updateDOM();
} else {
document.startViewTransition(() => updateDOM());
}
}