Khi một hiệu ứng chuyển đổi khung hiển thị xảy ra giữa hai tài liệu khác nhau, thì đó được gọi là hiệu ứng chuyển đổi khung hiển thị 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). Chrome hỗ trợ hiệu ứng chuyển đổi chế độ xem giữa các tài liệu từ Chrome 126.
Các hiệu ứng chuyển đổi chế độ xem giữa các tài liệu dựa trên chính những khối và nguyên tắc tạo dựng như hiệu ứng chuyển đổi chế độ xem trong cùng một tài liệu. Đây là điều rất có chủ ý:
- Trình duyệt sẽ chụp nhanh những phần tử có
view-transition-nameduy 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.
- Và cuối cùng, các hiệu ứng chuyển đổi được hỗ trợ bởi ảnh động CSS.
Điểm khác biệt so với hiệu ứng chuyển đổi chế độ xem trong cùng một tài liệu là bạn không cần gọi document.startViewTransition để bắt đầu hiệu ứng chuyển đổi chế độ xem khi dùng hiệu ứng chuyển đổi chế độ xem giữa các tài liệu. Thay vào đó, điều kiện kích hoạt cho hiệu ứng chuyển đổi chế độ xem giữa các tài liệu là một thao tác điều hướng cùng nguồn từ trang này sang trang khác, đây là 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 khung hiển thị giữa hai tài liệu. Tuy nhiên, bạn cần đáp ứng 2 điều kiện sau:
- Cả hai tài liệu đều phải có cùng nguồn gốc.
- Cả hai trang đều cần chọn tham gia để cho phép hiệu ứng chuyển đổi chế độ xem.
Cả hai điều kiện này đều được giải thích ở phần sau của tài liệu này.
Hiệu ứng chuyển đổi khung hiển thị giữa các tài liệu chỉ áp dụng cho các thao tác điều hướng có cùng nguồn gốc
Hiệu ứng chuyển đổi khung hiển thị 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. 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 giữa lược đồ, tên máy chủ và cổng được dùng, như được mô tả chi tiết trên web.dev.
Ví dụ: bạn có thể có một hiệu ứng chuyển đổi chế độ xem giữa các tài liệu khi di chuyển từ developer.chrome.com sang developer.chrome.com/blog, vì đây là các nguồn gốc giống nhau.
Bạn không thể có hiệu ứng chuyển đổi đó khi chuyển từ developer.chrome.com sang www.chrome.com, vì đó là các hiệu ứng chuyển đổi trên nhiều nguồn và cùng trang web.
Bạn có thể chọn sử dụng hiệu ứng chuyển đổi chế độ xem giữa các tài liệu
Để có hiệu ứng chuyển đổi chế độ xem giữa hai tài liệu, cả hai trang tham gia cần chọn cho phép hiệu ứng này. Bạn có thể thực hiện việc này bằng quy tắc @view-transition trong CSS.
Trong quy tắc @view-transition, hãy đặt giá trị mô tả navigation thành auto để bật hiệu ứng chuyển đổi chế độ xem cho các thao tác điều hướng nhiều tài liệu, cùng nguồn gốc.
@view-transition {
navigation: auto;
}
Bằng cách đặt giá trị mô tả navigation thành auto, bạn đang chọn cho phép các hiệu ứng chuyển đổi chế độ xem xảy ra cho các NavigationType sau đây:
traversepushhoặ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.
Ví dụ: những lượt điều hướng bị loại trừ khỏi auto là đ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 tải lại do người dùng hoặc tập lệnh khởi tạo.
Nếu một thao tác đ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 khung hiển thị sẽ bị bỏ qua bằng TimeoutError DOMException.
Bản minh hoạ hiệu ứng chuyển đổi chế độ xem giữa các tài liệu
Hãy xem bản minh hoạ sau đây 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, các hiệu ứng chuyển đổi khung hiển thị được kích hoạt bằng cách chuyển 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ể dùng một số tính năng của nền tảng web.
Những tính năng này không thuộc chính quy cách View Transition API, nhưng được thiết kế để sử dụng cùng với API này.
Các sự kiện pageswap và pagereveal
Để cho phép bạn tuỳ chỉnh hiệu ứng chuyển đổi chế độ xem giữa các tài liệu, quy cách HTML có hai sự kiện mới mà bạn có thể sử dụng: pageswap và pagereveal.
Hai sự kiện này sẽ được kích hoạt cho mọi thao tác điều hướng giữa các tài liệu cùng nguồn gốc, bất kể có sắp xảy ra hiệu ứng chuyển đổi chế độ xem hay không. Nếu hiệu ứng chuyển đổi khung 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
pageswapsẽ kích hoạt trước khi khung hình cuối cùng của một trang được hiển thị. Bạn có thể dùng sự kiện này để thực hiện một số thay đổi vào phút cuối trên trang đi, ngay trước khi các ảnh chụp nhanh cũ được chụp. - Sự kiện
pagerevealsẽ kích hoạt trên một trang sau khi trang đó được khởi tạo hoặc kích hoạt lại nhưng trước cơ hội hiển thị đầu tiên. Nhờ đó, bạn có thể tuỳ chỉnh trang mới trước khi chụp ảnh nhanh mới.
Ví dụ: bạn có thể sử dụng các sự kiện này để nhanh chóng đặt hoặc thay đổi 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 chế độ xem trước khi hiệu ứng này thực sự chạy.
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 hiệu ứng chuyển cảnh 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. Chúng cũng xử lý các promise khác nhau theo cách khác nhau:
pageswap: Sau khi tài liệu bị ẩn, đối tượngViewTransitioncũ sẽ bị bỏ qua. Khi đó,viewTransition.readysẽ từ chối vàviewTransition.finishedsẽ giải quyết.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 các promiseviewTransition.readyvàviewTransition.finished.
Thông tin kích hoạt chế độ chỉ đường
Trong cả sự kiện pageswap và pagereveal, bạn cũng có thể thực hiện hành động dựa trên URL của trang cũ và trang mới.
Ví dụ: trong MPA Stack Navigator, loại ảnh động cần dùng sẽ phụ thuộc vào đường dẫn điều hướng:
- Khi chuyển từ trang tổng quan sang trang chi tiết, nội dung mới cần trượt từ phải sang trái.
- Khi điều hướng từ trang chi tiết đến trang tổng quan, nội dung cũ cần trượt ra từ trái sang phải.
Để làm việc này, bạn cần có thông tin về thao tác điều hướng sắp xảy ra (trong trường hợp pageswap) hoặc vừa xảy ra (trong trường hợp pagereveal).
Để làm việc này, các trình duyệt hiện có thể hiển thị các đối tượng NavigationActivation lưu giữ thông tin về thao tác điều hướng cùng nguồn. Đối tượng này cho biết loại điều hướng đã dùng, mục nhập lịch sử đích đến hiện tại và đích đến cuối cùng như trong navigation.entries() từ Navigation API.
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 sự kiện 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 các giá trị view-transition-name trên những phần tử cần tham gia vào hiệu ứng chuyển đổi chế độ xem.
Bằng cách đó, bạn không cần phải trang trí từng mục trong danh sách bằng view-transition-name ngay từ đầu. Thay vào đó, điều này xảy ra ngay lập tức bằng JavaScript, chỉ trên những phần tử cần đến.
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';
}
}
});
Đoạn 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 khung hiển thị diễn ra. Bằng cách này, trang sẽ 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 duyệt qua nhật ký.
Để hỗ trợ việc này, hãy dùng hàm tiện ích này để tạm thời đặt 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 cách chặn hiển thị
Browser Support
Trong một số trường hợp, bạn có thể muốn trì hoãn lần hiển thị đầ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 mà bạn đang tạo hiệu ứng chuyển động là ổn định.
Trong <head>, hãy xác định một hoặc nhiều mã nhận dạng phần tử cần có trước khi trang kết xuất lần đầu, 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ụ: đối 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á thành true. Bản thân hình ảnh đó vẫn có thể đang tải.
Trước khi bạn sử dụng tính năng chặn hiển thị, hãy 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ị. Bạn cần đánh giá mức độ tác động của việc chặn hiển thị 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à đánh giá tác động của chỉ số này đối với người dùng bằng cách đo lường tác động đến Các chỉ số quan trọng về trang web.
Xem các loại hiệu ứng chuyển đổi trong hiệu ứng chuyển đổi chế độ xem giữa các tài liệu
Hiệu ứng chuyển đổi khung hiển thị giữa các tài liệu cũng hỗ trợ các loại hiệu ứng chuyển đổi khung hiển thị để tuỳ chỉnh ảnh động và những phần tử được chụp.
Ví dụ: khi chuyển đến trang tiếp theo hoặc trang trước trong một trình tự 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 có số thứ tự cao hơn hay 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 @view-transition:
@view-transition {
navigation: auto;
types: slide, forwards;
}
Để đặt các loại ngay lập tức, hãy sử dụng các sự kiện pageswap và pagereveal để điều chỉnh 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 loại này không 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 cần sử dụng trên ít nhất trang mới để các ả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 giả lớp :active-view-transition-type() theo cách tương tự như với hiệu ứng chuyển đổi khung hiển thị 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 hiệu ứng chuyển đổi khung hiển thị đang hoạt động, nên các loại sẽ tự động được dọn dẹp khi hiệu ứng chuyển đổi khung hiển thị kết thúc. Do đó, các loại hoạt động hiệu quả với những tính năng như BFCache.
Bản minh hoạ
Trong bản minh hoạ phân trang sau đây, nội dung trang sẽ trượt về phía trước hoặc phía sau dựa trên số trang mà bạn đang chuyển đến.
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 URL đích và URL nguồn.
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 công tác CSS trên GitHub kèm theo đề xuất và câu hỏi. Thêm tiền tố [css-view-transitions] vào vấn đề của bạn.
Nếu gặp phải lỗi, bạn nên gửi thông báo lỗi cho Chromium.