Khi quá trình chuyển đổi chế độ xem xảy ra giữa hai tài liệu khác nhau, quá trình này được gọi là chuyển đổi chế độ xem giữa các tài liệu. Đây thường là trường hợp trong các ứng dụng nhiều trang (MPA). Hiệu ứng chuyển đổi chế độ xem giữa các tài liệu được hỗ trợ trong Chrome từ phiên bản 126.
Hỗ trợ trình duyệt
Các hiệu ứng chuyển đổi chế độ xem giữa các tài liệu dựa trên các khối và nguyên tắc giống hệt như các hiệu ứng chuyển đổi chế độ xem trong cùng một tài liệu, điều này rất có chủ đích:
- Trình duyệt chụp nhanh các phần tử có
view-transition-name
duy nhất trên cả trang cũ và trang mới. - DOM được cập nhật trong khi quá trình kết xuất bị chặn.
- Cuối cùng, hiệu ứng chuyển đổi được cung cấp bởi ảnh động CSS.
Điểm khác biệt khi so sánh với các hiệu ứng chuyển đổi chế độ xem trong cùng một tài liệu là với các hiệu ứng chuyển đổi chế độ xem trên nhiều tài liệu, bạn không cần gọi document.startViewTransition
để bắt đầu hiệu ứng chuyển đổi chế độ xem. Thay vào đó, điều kiện kích hoạt cho quá trình chuyển đổi chế độ xem trên nhiều tài liệu là thao tác điều hướng cùng nguồn từ trang này sang trang khác, một thao tác mà người dùng trang web của bạn thường thực hiện bằng cách nhấp vào một đường liên kết.
Nói cách khác, không có API nào để gọi nhằm bắt đầu quá trình chuyển đổi chế độ xem giữa hai tài liệu. Tuy nhiên, bạn cần đáp ứng 2 điều kiện:
- Cả hai tài liệu đều phải tồn tại trên cùng một nguồn gốc.
- Cả hai trang đều cần chọn tham gia để cho phép chuyển đổi chế độ xem.
Cả hai điều kiện này sẽ được giải thích ở phần sau của tài liệu này.
Các hiệu ứng chuyển đổi chế độ xem giữa các tài liệu chỉ được giới hạn ở các thao tác điều hướng có cùng nguồn gốc
Hoạt động chuyển đổi chế độ xem giữa các tài liệu chỉ được giới hạn ở các thao tác điều hướng cùng nguồn gốc. Một thao tác điều hướng được coi là cùng nguồn gốc nếu nguồn gốc của cả hai trang tham gia đều giống nhau.
Nguồn gốc của một trang là sự kết hợp của lược đồ, tên máy chủ và cổng được sử dụng, như chi tiết trên web.dev.
Ví dụ: bạn có thể có quá trình chuyển đổi chế độ xem trên nhiều tài liệu khi điều hướng từ developer.chrome.com
đến developer.chrome.com/blog
, vì đó là các tài liệu có cùng nguồn gốc.
Bạn không thể tạo hiệu ứng chuyển đổi đó khi điều hướng từ developer.chrome.com
đến www.chrome.com
, vì các lượt chuyển đổi đó có nhiều nguồn gốc và cùng một trang web.
Bạn có thể chọn sử dụng tính năng chuyển đổi chế độ xem giữa các tài liệu
Để chuyển đổi chế độ xem trên nhiều tài liệu giữa hai tài liệu, cả hai trang tham gia cần phải chọn cho phép việc này. Bạn có thể thực hiện việc này bằng quy tắc tại @view-transition
trong CSS.
Trong quy tắc theo @view-transition
, hãy đặt chỉ số mô tả navigation
thành auto
để bật tính năng chuyển đổi chế độ xem cho các thao tác điều hướng cùng lúc trên nhiều tài liệu.
@view-transition {
navigation: auto;
}
Bằng cách đặt chỉ số mô tả navigation
thành auto
, bạn chọn cho phép các lượt chuyển đổi thành phần hiển thị xảy ra cho các NavigationType sau:
traverse
push
hoặcreplace
, nếu người dùng không kích hoạt thông qua các cơ chế giao diện người dùng của trình duyệt.
Các thành phần điều hướng bị loại trừ khỏi auto
, chẳng hạn như thao tác điều hướng bằng thanh địa chỉ URL hoặc nhấp vào dấu trang, cũng như mọi hình thức người dùng hoặc tập lệnh đã bắt đầu tải lại.
Nếu quá trình điều hướng mất quá nhiều thời gian (hơn 4 giây trong trường hợp của Chrome), thì quá trình chuyển đổi chế độ xem sẽ bị bỏ qua bằng TimeoutError
DOMException
.
Bản minh hoạ chuyển đổi chế độ xem giữa các tài liệu
Hãy tham khảo bản minh hoạ sau đây về việc sử dụng hiệu ứng chuyển đổi khung hiển thị để tạo bản minh hoạ Stack Navigator. Không có lệnh gọi nào đến document.startViewTransition()
ở đây, hiệu ứng chuyển đổi chế độ xem được kích hoạt bằng cách điều hướng từ trang này sang trang khác.
Tuỳ chỉnh hiệu ứng chuyển đổi chế độ xem giữa các tài liệu
Để tuỳ chỉnh hiệu ứng chuyển đổi chế độ xem giữa các tài liệu, bạn có thể sử dụng một số tính năng của nền tảng web.
Các tính năng này không phải là một phần của bản đặc tả API Chuyển đổi khung hiển thị, nhưng được thiết kế để sử dụng cùng với API này.
Sự kiện pageswap
và pagereveal
Để cho phép bạn tuỳ chỉnh các hiệu ứng chuyển đổi chế độ xem trên nhiều tài liệu, quy cách HTML bao gồm hai sự kiện mới mà bạn có thể sử dụng: pageswap
và pagereveal
.
Hai sự kiện này được kích hoạt cho mọi thao tác điều hướng giữa các tài liệu có cùng nguồn gốc, bất kể quá trình chuyển đổi chế độ xem sắp diễn ra hay không. Nếu một lượt chuyển đổi thành phần hiển thị sắp diễn ra giữa hai trang, bạn có thể truy cập vào đối tượng ViewTransition
bằng cách sử dụng thuộc tính viewTransition
trên các sự kiện này.
- Sự kiện
pageswap
sẽ kích hoạt trước khi khung cuối cùng của trang được hiển thị. Bạn có thể sử dụng tính năng này để thực hiện một số thay đổi vào phút chót trên trang gửi đi, ngay trước khi các ảnh chụp nhanh cũ được chụp. - Sự kiện
pagereveal
kích hoạt trên một trang sau khi được khởi chạy hoặc kích hoạt lại, nhưng trước cơ hội hiển thị đầu tiên. Với tính năng này, bạn có thể tuỳ chỉnh trang mới trước khi chụp ảnh nhanh.
Ví dụ: bạn có thể dùng các sự kiện này để đặt hoặc thay đổi nhanh một số giá trị view-transition-name
, hoặc truyền dữ liệu từ tài liệu này sang tài liệu khác bằng cách ghi và đọc dữ liệu từ sessionStorage
để tuỳ chỉnh hiệu ứng chuyển đổi khung hiển thị trước khi chạy thực sự.
let lastClickX, lastClickY;
document.addEventListener('click', (event) => {
if (event.target.tagName.toLowerCase() === 'a') return;
lastClickX = event.clientX;
lastClickY = event.clientY;
});
// Write position to storage on old page
window.addEventListener('pageswap', (event) => {
if (event.viewTransition && lastClick) {
sessionStorage.setItem('lastClickX', lastClickX);
sessionStorage.setItem('lastClickY', lastClickY);
}
});
// Read position from storage on new page
window.addEventListener('pagereveal', (event) => {
if (event.viewTransition) {
lastClickX = sessionStorage.getItem('lastClickX');
lastClickY = sessionStorage.getItem('lastClickY');
}
});
Nếu muốn, bạn có thể quyết định bỏ qua quá trình chuyển đổi trong cả hai sự kiện.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
if (goodReasonToSkipTheViewTransition()) {
e.viewTransition.skipTransition();
}
}
}
Đối tượng ViewTransition
trong pageswap
và pagereveal
là hai đối tượng khác nhau. Họ cũng xử lý nhiều lời hứa theo cách khác nhau:
pageswap
: Sau khi tài liệu bị ẩn, đối tượngViewTransition
cũ sẽ bị bỏ qua. Khi đó,viewTransition.ready
sẽ từ chối vàviewTransition.finished
sẽ phân giải.pagereveal
: Lời hứaupdateCallBack
đã được giải quyết tại thời điểm này. Bạn có thể sử dụng lời hứaviewTransition.ready
vàviewTransition.finished
.
Thông tin kích hoạt tính năng đi theo chỉ dẫn
Trong cả sự kiện pageswap
và pagereveal
, bạn cũng có thể hành động dựa trên URL của trang cũ và trang mới.
Ví dụ: trong Trình điều hướng ngăn xếp MPA, loại ảnh động cần sử dụng phụ thuộc vào đường dẫn điều hướng:
- Khi điều hướng từ trang tổng quan đến một trang chi tiết, nội dung mới cần phải trượt vào từ phải sang trái.
- Khi chuyển từ trang chi tiết sang trang tổng quan, nội dung cũ cần trượt từ trái sang phải.
Để thực hiện việc này, bạn cần thông tin về thao tác điều hướng, trong trường hợp pageswap
, sắp xảy ra hoặc trong trường hợp pagereveal
vừa xảy ra.
Để làm được điều này, các trình duyệt nay có thể hiển thị các đối tượng NavigationActivation
chứa thông tin về cách điều hướng cùng nguồn gốc. Đối tượng này hiển thị loại điều hướng đã sử dụng, mục hiện tại và mục nhật ký đích đến cuối cùng như trong navigation.entries()
từ API Điều hướng.
Trên một trang đã kích hoạt, bạn có thể truy cập vào đối tượng này thông qua navigation.activation
. Trong sự kiện pageswap
, bạn có thể truy cập vào thông tin này thông qua e.activation
.
Hãy xem bản minh hoạ Hồ sơ này sử dụng thông tin NavigationActivation
trong các sự kiện pageswap
và pagereveal
để đặt giá trị view-transition-name
trên các phần tử cần tham gia chuyển đổi chế độ xem.
Nhờ đó, bạn không phải trang trí trước từng mục trong danh sách bằng view-transition-name
. Thay vào đó, việc này sẽ xảy ra đúng lúc bằng cách sử dụng JavaScript, chỉ trên các phần tử cần JavaScript.
Sau đây là mã:
// OLD PAGE LOGIC
window.addEventListener('pageswap', async (e) => {
if (e.viewTransition) {
const targetUrl = new URL(e.activation.entry.url);
// Navigating to a profile page
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// Set view-transition-name values on the clicked row
document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';
// Remove view-transition-names after snapshots have been taken
// (this to deal with BFCache)
await e.viewTransition.finished;
document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';
}
}
});
// NEW PAGE LOGIC
window.addEventListener('pagereveal', async (e) => {
if (e.viewTransition) {
const fromURL = new URL(navigation.activation.from.url);
const currentURL = new URL(navigation.activation.entry.url);
// Navigating from a profile page back to the homepage
if (isProfilePage(fromURL) && isHomePage(currentURL)) {
const profile = extractProfileNameFromUrl(currentURL);
// Set view-transition-name values on the elements in the list
document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';
// Remove names after snapshots have been taken
// so that we're ready for the next navigation
await e.viewTransition.ready;
document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';
document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';
}
}
});
Mã này cũng tự dọn dẹp bằng cách xoá các giá trị view-transition-name
sau khi quá trình chuyển đổi chế độ xem chạy. Bằng cách này, trang đã sẵn sàng cho các thao tác điều hướng liên tiếp và cũng có thể xử lý việc truy cập vào nhật ký.
Để hỗ trợ bạn làm việc này, hãy sử dụng hàm tiện ích này để tạm thời đặt các view-transition-name
.
const setTemporaryViewTransitionNames = async (entries, vtPromise) => {
for (const [$el, name] of entries) {
$el.style.viewTransitionName = name;
}
await vtPromise;
for (const [$el, name] of entries) {
$el.style.viewTransitionName = '';
}
}
Giờ đây, bạn có thể đơn giản hoá mã trước đó như sau:
// OLD PAGE LOGIC
window.addEventListener('pageswap', async (e) => {
if (e.viewTransition) {
const targetUrl = new URL(e.activation.entry.url);
// Navigating to a profile page
if (isProfilePage(targetUrl)) {
const profile = extractProfileNameFromUrl(targetUrl);
// Set view-transition-name values on the clicked row
// Clean up after the page got replaced
setTemporaryViewTransitionNames([
[document.querySelector(`#${profile} span`), 'name'],
[document.querySelector(`#${profile} img`), 'avatar'],
], e.viewTransition.finished);
}
}
});
// NEW PAGE LOGIC
window.addEventListener('pagereveal', async (e) => {
if (e.viewTransition) {
const fromURL = new URL(navigation.activation.from.url);
const currentURL = new URL(navigation.activation.entry.url);
// Navigating from a profile page back to the homepage
if (isProfilePage(fromURL) && isHomePage(currentURL)) {
const profile = extractProfileNameFromUrl(currentURL);
// Set view-transition-name values on the elements in the list
// Clean up after the snapshots have been taken
setTemporaryViewTransitionNames([
[document.querySelector(`#${profile} span`), 'name'],
[document.querySelector(`#${profile} img`), 'avatar'],
], e.viewTransition.ready);
}
}
});
Chờ nội dung tải bằng tính năng chặn hiển thị
Hỗ trợ trình duyệt
Trong một số trường hợp, bạn có thể muốn tạm hoãn quá trình kết xuất đầu tiên của một trang cho đến khi một phần tử nhất định xuất hiện trong DOM mới. Điều này giúp tránh hiện tượng nhấp nháy và đảm bảo trạng thái bạn đang tạo ảnh động là ổn định.
Trong <head>
, hãy xác định một hoặc nhiều mã phần tử cần có trước khi trang hiển thị lần đầu tiên bằng cách sử dụng thẻ meta sau.
<link rel="expect" blocking="render" href="#section1">
Thẻ meta này có nghĩa là phần tử phải có trong DOM, chứ không phải nội dung phải được tải. Ví dụ: với hình ảnh, chỉ cần có thẻ <img>
với id
được chỉ định trong cây DOM là đủ để điều kiện đánh giá là đúng. Hệ thống có thể vẫn đang tải hình ảnh.
Trước khi bạn tìm hiểu toàn bộ về tính năng chặn hiển thị, xin lưu ý rằng hiển thị gia tăng là một khía cạnh cơ bản của Web, vì vậy hãy thận trọng khi chọn chặn hiển thị. Tác động của việc chặn hiển thị cần được đánh giá theo từng trường hợp. Theo mặc định, hãy tránh sử dụng blocking=render
trừ phi bạn có thể chủ động đo lường và đo lường mức độ tác động đối với người dùng, thông qua việc đo lường tác động đối với Các chỉ số quan trọng chính của trang web.
Xem các loại chuyển đổi trong chuyển đổi chế độ xem giữa các tài liệu
Hiệu ứng chuyển đổi chế độ xem giữa các tài liệu cũng hỗ trợ các loại hiệu ứng chuyển đổi chế độ xem để tuỳ chỉnh ảnh động và các thành phần được chụp.
Ví dụ: khi chuyển đến trang tiếp theo hoặc trang trước trong một quá trình phân trang, bạn có thể muốn sử dụng các ảnh động khác nhau tuỳ thuộc vào việc bạn đang chuyển đến trang cao hơn hay trang thấp hơn trong trình tự.
Để đặt trước các loại này, hãy thêm các loại đó vào quy tắc tại @view-transition
:
@view-transition {
navigation: auto;
types: slide, forwards;
}
Để thiết lập các loại ngay lập tức, hãy sử dụng các sự kiện pageswap
và pagereveal
để thao tác với giá trị của e.viewTransition.types
.
window.addEventListener("pagereveal", async (e) => {
if (e.viewTransition) {
const transitionType = determineTransitionType(navigation.activation.from, navigation.activation.entry);
e.viewTransition.types.add(transitionType);
}
});
Các kiểu không được tự động chuyển từ đối tượng ViewTransition
trên trang cũ sang đối tượng ViewTransition
của trang mới. Bạn cần xác định(các) loại để sử dụng trên ít nhất trang mới để ảnh động chạy như mong đợi.
Để phản hồi các loại này, hãy sử dụng bộ chọn lớp giả :active-view-transition-type()
theo cách tương tự như với các hiệu ứng chuyển đổi chế độ xem trong cùng một tài liệu
/* Determine what gets captured when the type is forwards or backwards */
html:active-view-transition-type(forwards, backwards) {
:root {
view-transition-name: none;
}
article {
view-transition-name: content;
}
.pagination {
view-transition-name: pagination;
}
}
/* Animation styles for forwards type only */
html:active-view-transition-type(forwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-left;
}
&::view-transition-new(content) {
animation-name: slide-in-from-right;
}
}
/* Animation styles for backwards type only */
html:active-view-transition-type(backwards) {
&::view-transition-old(content) {
animation-name: slide-out-to-right;
}
&::view-transition-new(content) {
animation-name: slide-in-from-left;
}
}
/* Animation styles for reload type only */
html:active-view-transition-type(reload) {
&::view-transition-old(root) {
animation-name: fade-out, scale-down;
}
&::view-transition-new(root) {
animation-delay: 0.25s;
animation-name: fade-in, scale-up;
}
}
Vì các loại chỉ áp dụng cho một lượt chuyển đổi thành phần hiển thị đang hoạt động, nên các loại sẽ tự động được dọn dẹp khi lượt chuyển đổi thành phần hiển thị kết thúc. Do đó, các loại hoạt động tốt với các tính năng như BFCache.
Bản minh hoạ
Trong bản minh hoạ phân trang sau, nội dung trang trượt tiến hoặc lùi dựa trên số trang mà bạn đang truy cập.
Loại chuyển đổi cần sử dụng được xác định trong các sự kiện pagereveal
và pageswap
bằng cách xem xét các URL đến và đi.
const determineTransitionType = (fromNavigationEntry, toNavigationEntry) => {
const currentURL = new URL(fromNavigationEntry.url);
const destinationURL = new URL(toNavigationEntry.url);
const currentPathname = currentURL.pathname;
const destinationPathname = destinationURL.pathname;
if (currentPathname === destinationPathname) {
return "reload";
} else {
const currentPageIndex = extractPageIndexFromPath(currentPathname);
const destinationPageIndex = extractPageIndexFromPath(destinationPathname);
if (currentPageIndex > destinationPageIndex) {
return 'backwards';
}
if (currentPageIndex < destinationPageIndex) {
return 'forwards';
}
return 'unknown';
}
};
Phản hồi
Chúng tôi luôn trân trọng ý kiến phản hồi của nhà phát triển. Để chia sẻ, hãy gửi vấn đề cho Nhóm làm việc về CSS trên GitHub kèm theo các đề xuất và câu hỏi. Đặt tiền tố [css-view-transitions]
cho vấn đề của bạn.
Nếu bạn gặp lỗi, hãy gửi lỗi Chromium.