Chuyển đổi chế độ xem nhiều tài liệu cho các ứng dụng nhiều trang

Khi quá trình chuyển đổi thành phần hiển thị 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 nhiều tài liệu. Trường hợp này thường xảy ra trong các ứng dụng nhiều trang (MPA). Chrome 126 hỗ trợ chuyển đổi chế độ xem nhiều tài liệu.

Hỗ trợ trình duyệt

  • 126
  • 126
  • x
  • x

Nguồn

Chuyển đổi chế độ xem nhiều tài liệu dựa trên cùng một khối dựng và nguyên tắc như chuyển đổi chế độ xem cùng một tài liệu, rất có chủ đích:

  1. 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.
  2. DOM được cập nhật trong khi quá trình kết xuất bị chặn.
  3. Và cuối cùng, hiệu ứng chuyển đổi được hỗ trợ bởi ảnh động CSS.

Điểm khác biệt khi so sánh với các lượt chuyển đổi chế độ xem cùng tài liệu là với các lượt chuyển đổi chế độ xem nhiều tài liệu, bạn không cần gọi document.startViewTransition để bắt đầu chuyển đổi chế độ xem. Thay vào đó, yếu tố kích hoạt cho việc chuyển đổi chế độ xem nhiều tài liệu là việc điều hướng cùng nguồn gốc từ trang này sang trang khác, một hành động thường do người dùng trang web của bạn nhấp vào đường liên kết thực hiện.

Nói cách khác, không có API nào cần gọi để bắt đầu quá trình chuyển đổi thành phần hiển thị giữa hai tài liệu. Tuy nhiên, có 2 điều kiện cần được đáp ứng:

  • Cả hai tài liệu cần phải tồn tại trên cùng một nguồn gốc.
  • Cả hai trang đều cần chọn sử dụng để 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.


Hoạt động chuyển đổi chế độ xem nhiều tài liệu bị giới hạn trong các lần điều hướng cùng nguồn gốc

Hoạt động chuyển đổi chế độ xem nhiều tài liệu chỉ bị giới hạn trong các thao tác điều hướng cùng nguồn gốc. Một điều hướng được xem là có cùng nguồn gốc nếu nguồn gốc của cả hai trang tham gia là 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 đã sử dụng, như thông tin chi tiết trên web.dev.

URL mẫu trong đó lược đồ, tên máy chủ và cổng được làm nổi bật. Khi kết hợp với nhau, chúng tạo thành nguồn gốc.
URL mẫu có lược đồ, tên máy chủ và cổng được làm nổi bật. Khi kết hợp với nhau, chúng tạo thành nguồn gốc.

Ví dụ: bạn có thể chuyển đổi chế độ xem nhiều tài liệu khi điều hướng từ developer.chrome.com sang developer.chrome.com/blog, vì các chế độ xem đó có cùng nguồn gốc. Bạn không thể thực hiện hiệu ứng chuyển đổi đó khi điều hướng từ developer.chrome.com sang www.chrome.com, vì đó là các hoạt động trên nhiều nguồn gốc và trên cùng một trang web.


Chọn sử dụng chuyển đổi chế độ xem nhiều tài liệu

Để chuyển đổi chế độ xem nhiều tài liệu giữa hai tài liệu, cả hai trang tham gia đều cần 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 @view-transition trong CSS.

Trong quy tắc @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 hoạt động điều hướng cùng nguồn gốc, trên nhiều tài liệu.

@view-transition {
  navigation: auto;
}

Khi đặt chỉ số mô tả navigation thành auto, bạn chọn cho phép quá trình chuyển đổi khung hiển thị diễn ra cho các NavigationType sau:

  • traverse
  • push hoặc replace, nếu người dùng không kích hoạt thông qua cơ chế giao diện người dùng của trình duyệt.

Các thao tác điều hướng bị loại trừ khỏi auto là điều hướng bằng cách sử dụng thanh địa chỉ URL hoặc nhấp vào dấu trang, cũng như bất kỳ hình thức người dùng hoặc tập lệnh nào bắt đầu tải lại.

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ì hiệu ứng chuyển đổi khung hiển thị sẽ bị bỏ qua bằng TimeoutError DOMException.

Bản minh hoạ chuyển đổi chế độ xem nhiều tài liệu

Hãy xem bản minh hoạ sau đây về cách sử dụng hiệu ứng chuyển đổi khung hiển thị để tạo bản minh hoạ Stack Navigator (Trình điều hướng ngăn xếp). Không có lệnh gọi đến document.startViewTransition() ở đây, quá trình chuyển đổi thành phần hiển thị được kích hoạt bằng cách điều hướng từ trang này sang trang khác.

Ghi lại bản minh hoạ Stack Navigator. Yêu cầu Chrome 126 trở lên.

Tuỳ chỉnh hiệu ứng chuyển đổi chế độ xem nhiều tài liệu

Để tuỳ chỉnh hiệu ứng chuyển đổi chế độ xem nhiều tài liệu, bạn có thể sử dụng một số tính năng trên nền tảng web.

Các tính năng này không thuộc chính quy cách của View Transition API, nhưng được thiết kế để sử dụng cùng với thông số kỹ thuật đó.

Sự kiện pageswappagereveal

Hỗ trợ trình duyệt

  • 124
  • 124
  • x
  • x

Nguồn

Để cho phép bạn tuỳ chỉnh các hiệu ứng chuyển đổi chế độ xem nhiều tài liệu, thông số kỹ thuật HTML bao gồm hai sự kiện mới mà bạn có thể sử dụng: pageswappagereveal.

Hai sự kiện này được kích hoạt cho mọi hoạt động điều hướng trên nhiều tài liệu có cùng nguồn gốc bất kể quá trình chuyển đổi chế độ xem có sắp xảy ra hay không. Nếu quá trình chuyển đổi khung hiển thị sắp xảy ra giữa hai trang này, 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 hình cuối cùng của trang 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 chụp ảnh chụp nhanh cũ.
  • Sự kiện pagereveal sẽ kích hoạt trên một trang sau khi trang đó được khởi chạy hoặc kích hoạt lại, nhưng trước cơ hội kết xuất đầu tiên. Nhờ công cụ này, 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 nó 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 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 pageswappagereveal là 2 đối tượng khác nhau. Họ cũng xử lý nhiều hứa hẹn theo cách khác nhau:

  • pageswap: Sau khi tài liệu bị ẩn, đối tượng ViewTransition cũ sẽ bị bỏ qua. Khi điều đó xảy ra, viewTransition.ready sẽ từ chối và viewTransition.finished sẽ giải quyết.
  • pagereveal: Lời hứa updateCallBack đã được giải quyết tại thời điểm này. Bạn có thể sử dụng lời hứa viewTransition.readyviewTransition.finished.

Hỗ trợ trình duyệt

  • 123
  • 123
  • x
  • x

Nguồn

Trong cả hai sự kiện pageswappagereveal, bạn cũng có thể thực hiện hành động dựa trên URL của các trang cũ và mới.

Ví dụ: trong MPA Stack Navigator (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 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 phải trượt ra từ trái sang phải.

Để thực hiện việc này, bạn cần thông tin về quá trình đ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 hiện có thể hiển thị các đối tượng NavigationActivation chứa thông tin về hoạt động điều hướng có cùng nguồn gốc. Đối tượng này hiển thị loại điều hướng đã dùng, mục nhập nhật ký đích đến hiện tại và cuối cùng như được tìm thấy 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 phần này thông qua e.activation.

Hãy xem Bản minh hoạ Hồ sơ này có sử dụng thông tin NavigationActivation trong các sự kiện pageswappagereveal để đặt giá trị view-transition-name trên các phần tử cần tham gia vào quá trình chuyển đổi chế độ xem.

Theo đó, bạn không phải trang trí từng mục trong danh sách bằng phần tử view-transition-name xuất hiện trước. Thay vào đó, điều này xảy ra đúng thời điểm bằng cách sử dụng JavaScript, chỉ trên các phần tử cần JavaScript.

Ghi lại bản minh hoạ Hồ sơ. Yêu cầu Chrome 126 trở lê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';
    }
  }
});

Mã này cũng tự dọn dẹp bằng cách xoá các giá trị view-transition-name sau khi chạy hiệu ứng chuyển đổi khung hiển thị. Bằng cách này, trang sẵn sàng để điều hướng liên tiếp và cũng có thể xử lý việc truyền tải lịch sử.

Để hỗ trợ điều này, hãy dùng hàm hiệu dụng 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 = '';
  }
}

Mã trước đây hiện có thể được đơn giản hoá 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

  • 124
  • 124
  • x
  • x

Nguồn

Trong một số trường hợp, bạn có thể tạm dừng lần hiển thị đầu tiên của 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 cài đặt ROM và đảm bảo trạng thái bạn đang tạo ảnh động ổn định.

Trong <head>, hãy dùng thẻ meta sau để xác định một hoặc nhiều mã phần tử cần có trước khi trang được kết xuất lần đầu tiên.

<link rel="expect" blocking="render" href="#section1">

Thẻ meta này có nghĩa là phần tử phải hiện diện trong DOM chứ không phải là nội dung phải được tải. Ví dụ: với hình ảnh, chỉ cần sự hiện diện của thẻ <img> cùng với id được chỉ định trong cây DOM là đủ để điều kiện được đánh giá là true (đúng). Bản thân hình ảnh đó vẫn có thể đang tải.

Trước khi 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ị. Cần đánh giá tác động của việc chặn kết xuất hình ảnh 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 dữ liệu đó đến người dùng bằng cách đo lường tác động đến các Các chỉ số quan trọng về trang web.


Xem các loại chuyển đổi trong quá trình chuyển đổi chế độ xem nhiều tài liệu

Hiệu ứng chuyển đổi chế độ xem nhiều tài liệu cũng hỗ trợ các loại chuyển đổi chế độ xem để tuỳ chỉnh ảnh động và phần tử nào đượ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 nhiều ảnh động tuỳ thuộc vào việc bạn sẽ 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 @view-transition:

@view-transition {
  navigation: auto;
  types: slide, forwards;
}

Để đặt loại nhanh, hãy sử dụng sự kiện pageswappagereveal để 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 loại này 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 cần sử dụng trên ít nhất là 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 lớp giả :active-view-transition-type() theo cách tương tự như với các chuyển đổi chế độ xem cùng 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 kiểu chỉ áp dụng cho quá trình chuyển đổi khung hiển thị đang hoạt động nên các kiểu sẽ tự động được xoá khi quá trình chuyển đổi khung hiển thị kết thúc. Do đó, các kiểu 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 đâ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 truy cập.

Ghi lại Bản minh hoạ phân trang (MPA). Giao diện này sử dụng các hiệu ứng chuyển đổi khác nhau tuỳ thuộc vào trang bạn sẽ truy cập.

Loại chuyển đổi cần sử dụng được xác định trong các sự kiện pagerevealpageswap bằng cách xem xét URL đến và từ URL.

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';
  }
};

Ý kiến 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 đề với Nhóm hoạt động 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 bạn gặp lỗi thì hãy gửi lỗi Chromium.