Hiệu ứng chuyển đổi chế độ xem cùng tài liệu cho các ứng dụng trang đơn

Ngày xuất bản: 17 tháng 8 năm 2021, Ngày cập nhật gần đây nhất: 25 tháng 9 năm 2024

Khi một hiệu ứng chuyển đổi chế độ xem chạy trên một tài liệu, hiệu ứng đó được gọi là hiệu ứng chuyển đổi chế độ xem cùng tài liệu. Trường hợp này thường xảy ra trong các ứng dụng trang đơn (SPA) mà JavaScript được dùng để cập nhật DOM. Kể từ Chrome 111, Chrome hỗ trợ các hiệu ứng chuyển đổi chế độ xem trong cùng một tài liệu.

Để kích hoạt quá trình chuyển đổi chế độ xem trong cùng một tài liệu, hãy gọi document.startViewTransition:

function handleClick(e) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    updateTheDOMSomehow();
    return;
  }

  // With a View Transition:
  document.startViewTransition(() => updateTheDOMSomehow());
}

Khi được gọi, trình duyệt sẽ tự động chụp ảnh nhanh tất cả các phần tử có thuộc tính CSS view-transition-name được khai báo trên đó.

Sau đó, hàm này sẽ thực thi lệnh gọi lại đã truyền để cập nhật DOM, sau đó chụp nhanh trạng thái mới.

Sau đó, các ảnh chụp nhanh này được sắp xếp trong một cây gồm các phần tử giả và tạo ảnh động bằng sức mạnh của ảnh động CSS. Các cặp ảnh chụp nhanh từ trạng thái cũ và mới chuyển đổi liền mạch từ vị trí và kích thước cũ sang vị trí mới, trong khi nội dung của chúng chuyển đổi hiệu ứng khuếch tán. Nếu muốn, bạn có thể sử dụng CSS để tuỳ chỉnh ảnh động.


Hiệu ứng chuyển đổi mặc định: Hiệu ứng chuyển đổi chéo

Hiệu ứng chuyển đổi thành phần hiển thị mặc định là hiệu ứng chuyển đổi lồng ghép, vì vậy, đây là một phần giới thiệu hay về API:

function spaNavigate(data) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    updateTheDOMSomehow(data);
    return;
  }

  // With a transition:
  document.startViewTransition(() => updateTheDOMSomehow(data));
}

Trong đó updateTheDOMSomehow thay đổi DOM thành trạng thái mới. Bạn có thể làm việc đó theo cách bạn muốn. Ví dụ: bạn có thể thêm hoặc xoá các phần tử, thay đổi tên lớp hoặc thay đổi kiểu.

Và chỉ như vậy, các trang sẽ chuyển đổi hiệu ứng:

Hiệu ứng chuyển tiếp mặc định. Bản minh hoạ tối giản. Nguồn.

Được rồi, hiệu ứng chuyển tiếp không ấn tượng lắm. Rất may, bạn có thể tuỳ chỉnh hiệu ứng chuyển đổi, nhưng trước tiên, bạn cần hiểu cách hoạt động của hiệu ứng chuyển đổi cơ bản này.


Cách hoạt động của các hiệu ứng chuyển đổi này

Hãy cập nhật mã mẫu trước đó.

document.startViewTransition(() => updateTheDOMSomehow(data));

Khi .startViewTransition() được gọi, API sẽ ghi lại trạng thái hiện tại của trang. Điều này bao gồm cả việc chụp ảnh nhanh.

Sau khi hoàn tất, lệnh gọi lại được truyền đến .startViewTransition() sẽ được gọi. Đó là nơi DOM được thay đổi. Sau đó, API sẽ ghi lại trạng thái mới của trang.

Sau khi thu thập trạng thái mới, API sẽ tạo một cây phần tử giả như sau:

::view-transition
└─ ::view-transition-group(root)
   └─ ::view-transition-image-pair(root)
      ├─ ::view-transition-old(root)
      └─ ::view-transition-new(root)

::view-transition nằm trong một lớp phủ, phủ lên mọi nội dung khác trên trang. Điều này rất hữu ích nếu bạn muốn đặt màu nền cho hiệu ứng chuyển đổi.

::view-transition-old(root) là ảnh chụp màn hình của thành phần hiển thị cũ và ::view-transition-new(root) là bản trình bày trực tiếp của thành phần hiển thị mới. Cả hai đều hiển thị dưới dạng "nội dung đã thay thế" CSS (như <img>).

Chế độ xem cũ tạo ảnh động từ opacity: 1 đến opacity: 0, trong khi chế độ xem mới tạo ảnh động từ opacity: 0 đến opacity: 1, tạo hiệu ứng chuyển đổi.

Tất cả ảnh động đều được thực hiện bằng ảnh động CSS, vì vậy, bạn có thể tuỳ chỉnh ảnh động bằng CSS.

Tuỳ chỉnh hiệu ứng chuyển đổi

Bạn có thể nhắm đến tất cả các phần tử giả lập chuyển đổi khung hiển thị bằng CSS. Vì ảnh động được xác định bằng CSS, nên bạn có thể sửa đổi ảnh động bằng các thuộc tính ảnh động CSS hiện có. Ví dụ:

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 5s;
}

Với một thay đổi đó, hiệu ứng mờ hiện diễn ra rất chậm:

Hiệu ứng chuyển đổi dài. Bản minh hoạ tối giản. Nguồn.

Được rồi, vẫn chưa ấn tượng. Thay vào đó, mã sau đây sẽ triển khai hiệu ứng chuyển đổi trục dùng chung của Material Design:

@keyframes fade-in {
  from { opacity: 0; }
}

@keyframes fade-out {
  to { opacity: 0; }
}

@keyframes slide-from-right {
  from { transform: translateX(30px); }
}

@keyframes slide-to-left {
  to { transform: translateX(-30px); }
}

::view-transition-old(root) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}

::view-transition-new(root) {
  animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}

Và đây là kết quả:

Chuyển đổi trục dùng chung. Bản minh hoạ tối giản. Nguồn.

Chuyển đổi nhiều phần tử

Trong bản minh hoạ trước, toàn bộ trang đều tham gia vào quá trình chuyển đổi trục dùng chung. Cách này hoạt động tốt cho hầu hết trang, nhưng có vẻ không phù hợp với tiêu đề vì tiêu đề sẽ trượt ra rồi trượt lại vào.

Để tránh điều này, bạn có thể trích xuất tiêu đề khỏi phần còn lại của trang để có thể tạo ảnh động riêng cho tiêu đề. Bạn có thể thực hiện việc này bằng cách gán view-transition-name cho phần tử.

.main-header {
  view-transition-name: main-header;
}

Giá trị của view-transition-name có thể là bất kỳ giá trị nào bạn muốn (ngoại trừ none, nghĩa là không có tên chuyển đổi). Mã này dùng để xác định riêng biệt phần tử trong quá trình chuyển đổi.

Và kết quả là:

Chuyển đổi trục dùng chung có tiêu đề cố định. Bản minh hoạ tối giản. Nguồn.

Giờ đây, tiêu đề sẽ giữ nguyên vị trí và chuyển đổi hiệu ứng.

Nội dung khai báo CSS đó đã khiến cây phần tử giả lập thay đổi:

::view-transition
├─ ::view-transition-group(root)
│  └─ ::view-transition-image-pair(root)
│     ├─ ::view-transition-old(root)
│     └─ ::view-transition-new(root)
└─ ::view-transition-group(main-header)
   └─ ::view-transition-image-pair(main-header)
      ├─ ::view-transition-old(main-header)
      └─ ::view-transition-new(main-header)

Hiện có hai nhóm chuyển đổi. Một cho tiêu đề và một cho phần còn lại. Bạn có thể nhắm mục tiêu các thành phần này một cách độc lập bằng CSS và cung cấp các hiệu ứng chuyển đổi khác nhau. Tuy nhiên, trong trường hợp này, main-header vẫn được giữ lại hiệu ứng chuyển đổi mặc định là hiệu ứng chuyển đổi lồng ghép.

Được rồi, hiệu ứng chuyển đổi mặc định không chỉ là hiệu ứng chuyển đổi lồng ghép, ::view-transition-group cũng chuyển đổi:

  • Vị trí và biến đổi (sử dụng transform)
  • Chiều rộng
  • Chiều cao

Điều đó không quan trọng cho đến bây giờ, vì tiêu đề có cùng kích thước và vị trí ở cả hai bên của thay đổi DOM. Tuy nhiên, bạn cũng có thể trích xuất văn bản trong tiêu đề:

.main-header-text {
  view-transition-name: main-header-text;
  width: fit-content;
}

fit-content được dùng để phần tử có kích thước bằng văn bản, thay vì kéo giãn theo chiều rộng còn lại. Nếu không có thuộc tính này, mũi tên quay lại sẽ làm giảm kích thước của phần tử văn bản tiêu đề, thay vì có cùng kích thước trên cả hai trang.

Vậy là giờ đây, chúng ta có 3 phần để chơi:

::view-transition
├─ ::view-transition-group(root)
│  └─ …
├─ ::view-transition-group(main-header)
│  └─ …
└─ ::view-transition-group(main-header-text)
   └─ …

Nhưng xin nhắc lại, bạn chỉ cần sử dụng các giá trị mặc định:

Văn bản tiêu đề trượt. Bản minh hoạ tối giản. Nguồn.

Bây giờ, văn bản tiêu đề sẽ trượt một chút để tạo không gian cho nút quay lại.


Tạo ảnh động cho nhiều phần tử giả lập theo cùng một cách bằng view-transition-class

Hỗ trợ trình duyệt

  • Chrome: 125.
  • Edge: 125.
  • Firefox: không được hỗ trợ.
  • Bản xem trước công nghệ Safari: được hỗ trợ.

Giả sử bạn có một hiệu ứng chuyển đổi thành phần hiển thị với một loạt thẻ nhưng cũng có một tiêu đề trên trang. Để tạo ảnh động cho tất cả các thẻ ngoại trừ tiêu đề, bạn phải viết một bộ chọn nhắm đến từng thẻ riêng lẻ.

h1 {
    view-transition-name: title;
}
::view-transition-group(title) {
    animation-timing-function: ease-in-out;
}

#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
…
#card20 { view-transition-name: card20; }

::view-transition-group(card1),
::view-transition-group(card2),
::view-transition-group(card3),
::view-transition-group(card4),
…
::view-transition-group(card20) {
    animation-timing-function: var(--bounce);
}

Có 20 phần tử? Đó là 20 bộ chọn bạn cần viết. Thêm phần tử mới? Sau đó, bạn cũng cần mở rộng bộ chọn áp dụng các kiểu ảnh động. Không thể mở rộng quy mô một cách chính xác.

Bạn có thể sử dụng view-transition-class trong các phần tử giả lập chuyển đổi thành phần hiển thị để áp dụng cùng một quy tắc kiểu.

#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
#card5 { view-transition-name: card5; }
…
#card20 { view-transition-name: card20; }

#cards-wrapper > div {
  view-transition-class: card;
}
html::view-transition-group(.card) {
  animation-timing-function: var(--bounce);
}

Ví dụ về thẻ sau tận dụng đoạn mã CSS trước đó. Tất cả thẻ (bao gồm cả thẻ mới thêm) đều được áp dụng cùng một thời gian bằng một bộ chọn: html::view-transition-group(.card).

Bản ghi minh hoạ về Thẻ. Khi sử dụng view-transition-class, lớp này sẽ áp dụng cùng một animation-timing-function cho tất cả các thẻ, ngoại trừ các thẻ đã thêm hoặc xoá.

Gỡ lỗi hiệu ứng chuyển đổi

Vì hiệu ứng chuyển đổi khung hiển thị được xây dựng dựa trên ảnh động CSS, nên bảng điều khiển Animations (Ảnh động) trong Công cụ dành cho nhà phát triển của Chrome rất phù hợp để gỡ lỗi hiệu ứng chuyển đổi.

Khi sử dụng bảng điều khiển Animations (Ảnh động), bạn có thể tạm dừng ảnh động tiếp theo, sau đó tua đi và tua lại ảnh động đó. Trong thời gian này, bạn có thể tìm thấy các phần tử giả lập chuyển đổi trong bảng điều khiển Elements (Thành phần).

Gỡ lỗi chuyển đổi thành phần hiển thị bằng Công cụ của Chrome cho nhà phát triển.

Các phần tử chuyển đổi không cần phải là cùng một phần tử DOM

Cho đến nay, chúng ta đã sử dụng view-transition-name để tạo các phần tử chuyển đổi riêng biệt cho tiêu đề và văn bản trong tiêu đề. Về mặt lý thuyết, đây là cùng một phần tử trước và sau khi DOM thay đổi, nhưng bạn có thể tạo hiệu ứng chuyển đổi khi không phải như vậy.

Ví dụ: bạn có thể cung cấp view-transition-name cho video nhúng chính:

.full-embed {
  view-transition-name: full-embed;
}

Sau đó, khi người dùng nhấp vào hình thu nhỏ, hình thu nhỏ đó có thể được cấp cùng một view-transition-name, chỉ trong thời gian chuyển đổi:

thumbnail.onclick = async () => {
  thumbnail.style.viewTransitionName = 'full-embed';

  document.startViewTransition(() => {
    thumbnail.style.viewTransitionName = '';
    updateTheDOMSomehow();
  });
};

Và kết quả:

Một phần tử chuyển đổi sang phần tử khác. Bản minh hoạ tối giản. Nguồn.

Hình thu nhỏ hiện chuyển đổi thành hình ảnh chính. Mặc dù về mặt khái niệm (và theo nghĩa đen) là các phần tử khác nhau, nhưng API chuyển đổi coi chúng là giống nhau vì chúng có chung view-transition-name.

Mã thực tế cho quá trình chuyển đổi này phức tạp hơn một chút so với ví dụ trước, vì mã này cũng xử lý quá trình chuyển đổi trở lại trang hình thu nhỏ. Xem nguồn để biết cách triển khai đầy đủ.


Hiệu ứng chuyển đổi tuỳ chỉnh khi vào và thoát

Hãy xem ví dụ sau:

Vào và thoát khỏi thanh bên. Bản minh hoạ tối giản. Nguồn.

Thanh bên là một phần của quá trình chuyển đổi:

.sidebar {
  view-transition-name: sidebar;
}

Tuy nhiên, không giống như tiêu đề trong ví dụ trước, thanh bên không xuất hiện trên tất cả các trang. Nếu cả hai trạng thái đều có thanh bên, thì các phần tử giả lập chuyển đổi sẽ có dạng như sau:

::view-transition
├─ …other transition groups…
└─ ::view-transition-group(sidebar)
   └─ ::view-transition-image-pair(sidebar)
      ├─ ::view-transition-old(sidebar)
      └─ ::view-transition-new(sidebar)

Tuy nhiên, nếu thanh bên chỉ có trên trang mới, thì phần tử giả ::view-transition-old(sidebar) sẽ không có trên trang đó. Vì không có hình ảnh "cũ" cho thanh bên, nên cặp hình ảnh sẽ chỉ có ::view-transition-new(sidebar). Tương tự, nếu thanh bên chỉ có trên trang cũ, thì cặp hình ảnh sẽ chỉ có ::view-transition-old(sidebar).

Trong bản minh hoạ trước, thanh bên chuyển đổi theo cách khác nhau tuỳ thuộc vào việc thanh bên đang vào, thoát hoặc hiển thị ở cả hai trạng thái. Cửa sổ này sẽ xuất hiện bằng cách trượt từ bên phải và mờ dần, thoát bằng cách trượt sang bên phải và mờ dần, đồng thời giữ nguyên vị trí khi xuất hiện ở cả hai trạng thái.

Để tạo các hiệu ứng chuyển đổi cụ thể khi vào và khi thoát, bạn có thể sử dụng lớp giả :only-child để nhắm mục tiêu các phần tử giả cũ hoặc mới khi đó là phần tử con duy nhất trong cặp hình ảnh:

/* Entry transition */
::view-transition-new(sidebar):only-child {
  animation: 300ms cubic-bezier(0, 0, 0.2, 1) both fade-in,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}

/* Exit transition */
::view-transition-old(sidebar):only-child {
  animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;
}

Trong trường hợp này, không có quá trình chuyển đổi cụ thể nào khi thanh bên xuất hiện ở cả hai trạng thái, vì trạng thái mặc định là hoàn hảo.

Cập nhật DOM không đồng bộ và chờ nội dung

Lệnh gọi lại được truyền đến .startViewTransition() có thể trả về một lời hứa, cho phép cập nhật DOM không đồng bộ và chờ nội dung quan trọng sẵn sàng.

document.startViewTransition(async () => {
  await something;
  await updateTheDOMSomehow();
  await somethingElse;
});

Quá trình chuyển đổi sẽ không bắt đầu cho đến khi lời hứa được thực hiện. Trong thời gian này, trang sẽ bị treo, vì vậy, bạn nên giảm thiểu độ trễ ở đây. Cụ thể, bạn nên thực hiện các lệnh tìm nạp mạng trước khi gọi .startViewTransition(), trong khi trang vẫn tương tác đầy đủ, thay vì thực hiện các lệnh tìm nạp này trong lệnh gọi lại .startViewTransition().

Nếu bạn quyết định chờ hình ảnh hoặc phông chữ sẵn sàng, hãy nhớ sử dụng thời gian chờ mạnh mẽ:

const wait = ms => new Promise(r => setTimeout(r, ms));

document.startViewTransition(async () => {
  updateTheDOMSomehow();

  // Pause for up to 100ms for fonts to be ready:
  await Promise.race([document.fonts.ready, wait(100)]);
});

Tuy nhiên, trong một số trường hợp, tốt hơn hết bạn nên tránh tình trạng trễ và sử dụng nội dung hiện có.


Khai thác tối đa nội dung bạn đã có

Trong trường hợp hình thu nhỏ chuyển đổi sang hình ảnh lớn hơn:

Hình thu nhỏ chuyển đổi sang hình ảnh lớn hơn. Dùng thử trang web minh hoạ.

Hiệu ứng chuyển đổi mặc định là hiệu ứng chuyển đổi lồng ghép, tức là hình thu nhỏ có thể chuyển đổi lồng ghép với hình ảnh đầy đủ chưa tải.

Một cách để xử lý vấn đề này là đợi hình ảnh tải xong trước khi bắt đầu chuyển đổi. Tốt nhất là bạn nên thực hiện việc này trước khi gọi .startViewTransition() để trang vẫn có thể tương tác và có thể hiển thị một vòng quay để cho người dùng biết rằng mọi thứ đang tải. Nhưng trong trường hợp này, có một cách tốt hơn:

::view-transition-old(full-embed),
::view-transition-new(full-embed) {
  /* Prevent the default animation,
  so both views remain opacity:1 throughout the transition */
  animation: none;
  /* Use normal blending,
  so the new view sits on top and obscures the old view */
  mix-blend-mode: normal;
}

Giờ đây, hình thu nhỏ không mờ đi mà chỉ nằm bên dưới hình ảnh đầy đủ. Điều này có nghĩa là nếu chế độ xem mới chưa tải, hình thu nhỏ sẽ hiển thị trong suốt quá trình chuyển đổi. Điều này có nghĩa là quá trình chuyển đổi có thể bắt đầu ngay lập tức và hình ảnh đầy đủ có thể tải theo thời gian riêng.

Cách này sẽ không hoạt động nếu chế độ xem mới có tính năng trong suốt, nhưng trong trường hợp này, chúng ta biết rằng chế độ xem đó không có tính năng này, vì vậy, chúng ta có thể thực hiện việc tối ưu hoá này.

Xử lý các thay đổi về tỷ lệ khung hình

Cho đến nay, tất cả các hiệu ứng chuyển đổi đều được áp dụng cho các phần tử có cùng tỷ lệ khung hình, nhưng không phải lúc nào cũng vậy. Nếu hình thu nhỏ có tỷ lệ 1:1 và hình ảnh chính có tỷ lệ 16:9 thì sao?

Một phần tử chuyển đổi sang phần tử khác, với tỷ lệ khung hình thay đổi. Bản minh hoạ tối giản. Nguồn.

Trong hiệu ứng chuyển đổi mặc định, nhóm sẽ tạo ảnh động từ kích thước trước đến kích thước sau. Chế độ xem cũ và mới có chiều rộng 100% của nhóm và chiều cao tự động, nghĩa là các chế độ xem này giữ nguyên tỷ lệ khung hình bất kể kích thước của nhóm.

Đây là giá trị mặc định phù hợp, nhưng không phải là giá trị mong muốn trong trường hợp này. Do đó:

::view-transition-old(full-embed),
::view-transition-new(full-embed) {
  /* Prevent the default animation,
  so both views remain opacity:1 throughout the transition */
  animation: none;
  /* Use normal blending,
  so the new view sits on top and obscures the old view */
  mix-blend-mode: normal;
  /* Make the height the same as the group,
  meaning the view size might not match its aspect-ratio. */
  height: 100%;
  /* Clip any overflow of the view */
  overflow: clip;
}

/* The old view is the thumbnail */
::view-transition-old(full-embed) {
  /* Maintain the aspect ratio of the view,
  by shrinking it to fit within the bounds of the element */
  object-fit: contain;
}

/* The new view is the full image */
::view-transition-new(full-embed) {
  /* Maintain the aspect ratio of the view,
  by growing it to cover the bounds of the element */
  object-fit: cover;
}

Điều này có nghĩa là hình thu nhỏ vẫn nằm ở giữa phần tử khi chiều rộng mở rộng, nhưng hình ảnh đầy đủ sẽ "không bị cắt" khi chuyển đổi từ 1:1 sang 16:9.

Để biết thêm thông tin chi tiết, hãy xem bài viết Chuyển đổi thành phần hiển thị: Xử lý các thay đổi về tỷ lệ khung hình


Sử dụng truy vấn nội dung nghe nhìn để thay đổi hiệu ứng chuyển đổi cho các trạng thái thiết bị

Bạn có thể muốn sử dụng các hiệu ứng chuyển đổi khác nhau trên thiết bị di động so với máy tính, chẳng hạn như ví dụ sau đây thực hiện một hiệu ứng trượt toàn bộ từ bên cạnh trên thiết bị di động, nhưng trượt một cách tinh tế hơn trên máy tính:

Một phần tử chuyển đổi sang phần tử khác. Bản minh hoạ tối giản. Nguồn.

Bạn có thể thực hiện việc này bằng cách sử dụng các truy vấn nội dung nghe nhìn thông thường:

/* Transitions for mobile */
::view-transition-old(root) {
  animation: 300ms ease-out both full-slide-to-left;
}

::view-transition-new(root) {
  animation: 300ms ease-out both full-slide-from-right;
}

@media (min-width: 500px) {
  /* Overrides for larger displays.
  This is the shared axis transition from earlier in the article. */
  ::view-transition-old(root) {
    animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
      300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
  }

  ::view-transition-new(root) {
    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
      300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
  }
}

Bạn cũng có thể thay đổi phần tử mà bạn chỉ định view-transition-name tuỳ thuộc vào các truy vấn nội dung nghe nhìn trùng khớp.


Phản ứng với lựa chọn ưu tiên "giảm chuyển động"

Người dùng có thể cho biết họ muốn giảm chuyển động thông qua hệ điều hành và lựa chọn ưu tiên đó sẽ hiển thị trong CSS.

Bạn có thể chọn ngăn mọi quá trình chuyển đổi cho những người dùng này:

@media (prefers-reduced-motion) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

Tuy nhiên, lựa chọn ưu tiên "giảm chuyển động" không có nghĩa là người dùng muốn không có chuyển động. Thay vì đoạn mã trước, bạn có thể chọn một ảnh động tinh tế hơn nhưng vẫn thể hiện mối quan hệ giữa các phần tử và luồng dữ liệu.


Xử lý nhiều kiểu chuyển đổi thành phần hiển thị bằng các loại chuyển đổi thành phần hiển thị

Hỗ trợ trình duyệt

  • Chrome: 125.
  • Edge: 125.
  • Firefox: không được hỗ trợ.
  • Safari: 18.

Đôi khi, quá trình chuyển đổi từ một thành phần hiển thị cụ thể sang một thành phần hiển thị khác phải có quá trình chuyển đổi được điều chỉnh riêng. 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 trượt nội dung theo hướ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ự.

Bản ghi Minh hoạ phân trang. Chuyển đổi này sử dụng các hiệu ứng chuyển đổi khác nhau tuỳ thuộc vào trang bạn đang chuyển đến.

Để làm việc này, bạn có thể sử dụng các loại chuyển đổi khung hiển thị. Các loại này cho phép bạn chỉ định một hoặc nhiều loại cho một chuyển đổi khung hiển thị đang hoạt động. Ví dụ: khi chuyển sang một trang cao hơn trong trình tự phân trang, hãy sử dụng loại forwards và khi chuyển sang một trang thấp hơn, hãy sử dụng loại backwards. Các loại này chỉ hoạt động khi chụp hoặc thực hiện chuyển đổi, đồng thời bạn có thể tuỳ chỉnh từng loại thông qua CSS để sử dụng các ảnh động khác nhau.

Để sử dụng các loại trong quá trình chuyển đổi chế độ xem cùng một tài liệu, bạn truyền types vào phương thức startViewTransition. Để cho phép việc này, document.startViewTransition cũng chấp nhận một đối tượng: update là hàm gọi lại cập nhật DOM và types là một mảng có các loại.

const direction = determineBackwardsOrForwards();

const t = document.startViewTransition({
  update: updateTheDOMSomehow,
  types: ['slide', direction],
});

Để phản hồi các loại này, hãy sử dụng bộ chọn :active-view-transition-type(). Truyền type mà bạn muốn nhắm đến vào bộ chọn. Điều này cho phép bạn tách biệt các kiểu của nhiều hiệu ứng chuyển đổi thành phần hiển thị với nhau mà không làm cho nội dung khai báo của hiệu ứng này ảnh hưởng đến nội dung khai báo của hiệu ứng khác.

Vì các loại chỉ áp dụng khi chụp hoặc thực hiện chuyển đổi, nên bạn có thể sử dụng bộ chọn để đặt hoặc huỷ đặt view-transition-name trên một phần tử chỉ cho hiệu ứng chuyển đổi thành phần hiển thị có loại đó.

/* 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 (using the default root snapshot) */
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;
  }
}

Trong bản minh hoạ phân trang sau đây, nội dung trang sẽ trượt về trước hoặc về sau dựa trên số trang mà bạn đang chuyển đến. Các loại được xác định khi người dùng nhấp vào, sau đó các loại này được truyền vào document.startViewTransition.

Để nhắm đến bất kỳ quá trình chuyển đổi khung hiển thị nào đang hoạt động, bất kể loại nào, bạn có thể sử dụng bộ chọn lớp giả :active-view-transition.

html:active-view-transition {
    …
}

Xử lý nhiều kiểu chuyển đổi thành phần hiển thị bằng tên lớp trên gốc chuyển đổi thành phần hiển thị

Đôi khi, quá trình chuyển đổi từ một loại chế độ xem cụ thể sang một loại chế độ xem khác phải có quá trình chuyển đổi được điều chỉnh riêng. Hoặc thao tác điều hướng "quay lại" phải khác với thao tác điều hướng "tiến lên".

Các hiệu ứng chuyển đổi khác nhau khi quay lại. Bản minh hoạ tối giản. Nguồn.

Trước khi có các loại chuyển đổi, cách xử lý những trường hợp này là tạm thời đặt tên lớp trên gốc chuyển đổi. Khi gọi document.startViewTransition, phần tử gốc chuyển đổi này là phần tử <html>, có thể truy cập bằng document.documentElement trong JavaScript:

if (isBackNavigation) {
  document.documentElement.classList.add('back-transition');
}

const transition = document.startViewTransition(() =>
  updateTheDOMSomehow(data)
);

try {
  await transition.finished;
} finally {
  document.documentElement.classList.remove('back-transition');
}

Để xoá các lớp sau khi quá trình chuyển đổi kết thúc, ví dụ này sử dụng transition.finished, một lời hứa sẽ giải quyết sau khi quá trình chuyển đổi đã đạt đến trạng thái kết thúc. Các thuộc tính khác của đối tượng này được đề cập trong tài liệu tham khảo API.

Bây giờ, bạn có thể sử dụng tên lớp đó trong CSS để thay đổi hiệu ứng chuyển đổi:

/* 'Forward' transitions */
::view-transition-old(root) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}

::view-transition-new(root) {
  animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, 300ms
      cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}

/* Overrides for 'back' transitions */
.back-transition::view-transition-old(root) {
  animation-name: fade-out, slide-to-right;
}

.back-transition::view-transition-new(root) {
  animation-name: fade-in, slide-from-left;
}

Cũng như với truy vấn nội dung đa phương tiện, sự hiện diện của các lớp này cũng có thể được dùng để thay đổi phần tử nào nhận được view-transition-name.


Chạy hiệu ứng chuyển đổi mà không làm ảnh động khác bị treo

Hãy xem bản minh hoạ vị trí chuyển đổi video sau:

Hiệu ứng chuyển đổi video. Bản minh hoạ tối giản. Nguồn.

Bạn có thấy vấn đề gì không? Đừng lo nếu bạn chưa làm. Sau đây là video được làm chậm:

Chuyển đổi video, chậm hơn. Bản minh hoạ tối giản. Nguồn.

Trong quá trình chuyển đổi, video có vẻ như bị treo, sau đó phiên bản phát của video sẽ mờ dần. Lý do là ::view-transition-old(video) là ảnh chụp màn hình của thành phần hiển thị cũ, trong khi ::view-transition-new(video) là hình ảnh trực tiếp của thành phần hiển thị mới.

Bạn có thể khắc phục vấn đề này, nhưng trước tiên, hãy tự hỏi xem có đáng sửa hay không. Nếu bạn không thấy "vấn đề" khi hiệu ứng chuyển đổi đang phát ở tốc độ bình thường, thì bạn không cần phải thay đổi hiệu ứng đó.

Nếu bạn thực sự muốn khắc phục vấn đề này, đừng hiển thị ::view-transition-old(video); hãy chuyển thẳng sang ::view-transition-new(video). Bạn có thể thực hiện việc này bằng cách ghi đè các kiểu và ảnh động mặc định:

::view-transition-old(video) {
  /* Don't show the frozen old view */
  display: none;
}

::view-transition-new(video) {
  /* Don't fade the new view in */
  animation: none;
}

Chỉ vậy thôi!

Chuyển đổi video, chậm hơn. Bản minh hoạ tối giản. Nguồn.

Giờ đây, video sẽ phát trong suốt quá trình chuyển đổi.


Tích hợp với Navigation API (và các khung khác)

Các hiệu ứng chuyển đổi khung hiển thị được chỉ định theo cách có thể tích hợp với các khung hoặc thư viện khác. Ví dụ: nếu ứng dụng một trang (SPA) của bạn đang sử dụng một bộ định tuyến, bạn có thể điều chỉnh cơ chế cập nhật của bộ định tuyến để cập nhật nội dung bằng cách sử dụng quá trình chuyển đổi chế độ xem.

Trong đoạn mã sau đây được lấy từ bản minh hoạ phân trang này, trình xử lý chặn của Navigation API được điều chỉnh để gọi document.startViewTransition khi các lượt chuyển đổi thành phần hiển thị được hỗ trợ.

navigation.addEventListener("navigate", (e) => {
    // Don't intercept if not needed
    if (shouldNotIntercept(e)) return;

    // Intercept the navigation
    e.intercept({
        handler: async () => {
            // Fetch the new content
            const newContent = await fetchNewContent(e.destination.url, {
                signal: e.signal,
            });

            // The UA does not support View Transitions, or the UA
            // already provided a Visual Transition by itself (e.g. swipe back).
            // In either case, update the DOM directly
            if (!document.startViewTransition || e.hasUAVisualTransition) {
                setContent(newContent);
                return;
            }

            // Update the content using a View Transition
            const t = document.startViewTransition(() => {
                setContent(newContent);
            });
        }
    });
});

Một số trình duyệt (nhưng không phải tất cả) cung cấp hiệu ứng chuyển đổi riêng khi người dùng thực hiện cử chỉ vuốt để điều hướng. Trong trường hợp đó, bạn không nên kích hoạt quá trình chuyển đổi khung hiển thị của riêng mình vì điều này sẽ dẫn đến trải nghiệm người dùng kém hoặc gây nhầm lẫn. Người dùng sẽ thấy hai hiệu ứng chuyển đổi (một do trình duyệt cung cấp và một do bạn cung cấp) chạy liên tiếp.

Do đó, bạn nên ngăn quá trình chuyển đổi thành phần hiển thị bắt đầu khi trình duyệt đã cung cấp quá trình chuyển đổi hình ảnh riêng. Để làm được việc này, hãy kiểm tra giá trị của thuộc tính hasUAVisualTransition của thực thể NavigateEvent. Thuộc tính này được đặt thành true khi trình duyệt đã cung cấp hiệu ứng chuyển đổi hình ảnh. Thuộc tính hasUIVisualTransition này cũng tồn tại trên các thực thể PopStateEvent.

Trong đoạn mã trước, việc kiểm tra để xác định xem có chạy hiệu ứng chuyển đổi thành phần hiển thị hay không sẽ tính đến thuộc tính này. Khi không hỗ trợ chuyển đổi chế độ xem trong cùng một tài liệu hoặc khi trình duyệt đã cung cấp chuyển đổi riêng, thì chuyển đổi chế độ xem sẽ bị bỏ qua.

if (!document.startViewTransition || e.hasUAVisualTransition) {
  setContent(newContent);
  return;
}

Trong bản ghi sau, người dùng vuốt để quay lại trang trước. Bản ghi ở bên trái không bao gồm việc kiểm tra cờ hasUAVisualTransition. Bản ghi ở bên phải có bao gồm thao tác kiểm tra, do đó bỏ qua quá trình chuyển đổi chế độ xem thủ công vì trình duyệt đã cung cấp quá trình chuyển đổi hình ảnh.

So sánh cùng một trang web không có (bên trái) và có (bên phải) việc kiểm tra hasUAVisualTransition

Tạo ảnh động bằng JavaScript

Cho đến nay, tất cả các hiệu ứng chuyển đổi đều được xác định bằng CSS, nhưng đôi khi CSS là không đủ:

Chuyển đổi vòng tròn. Bản minh hoạ tối giản. Nguồn.

Bạn không thể thực hiện một số phần của quá trình chuyển đổi này chỉ bằng CSS:

  • Ảnh động bắt đầu từ vị trí nhấp.
  • Ảnh động kết thúc bằng vòng tròn có bán kính đến góc xa nhất. Tuy nhiên, hy vọng rằng điều này sẽ có thể thực hiện được với CSS trong tương lai.

Rất may, bạn có thể tạo hiệu ứng chuyển đổi bằng Web Animation API!

let lastClick;
addEventListener('click', event => (lastClick = event));

function spaNavigate(data) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    updateTheDOMSomehow(data);
    return;
  }

  // Get the click position, or fallback to the middle of the screen
  const x = lastClick?.clientX ?? innerWidth / 2;
  const y = lastClick?.clientY ?? innerHeight / 2;
  // Get the distance to the furthest corner
  const endRadius = Math.hypot(
    Math.max(x, innerWidth - x),
    Math.max(y, innerHeight - y)
  );

  // With a transition:
  const transition = document.startViewTransition(() => {
    updateTheDOMSomehow(data);
  });

  // Wait for the pseudo-elements to be created:
  transition.ready.then(() => {
    // Animate the root's new view
    document.documentElement.animate(
      {
        clipPath: [
          `circle(0 at ${x}px ${y}px)`,
          `circle(${endRadius}px at ${x}px ${y}px)`,
        ],
      },
      {
        duration: 500,
        easing: 'ease-in',
        // Specify which pseudo-element to animate
        pseudoElement: '::view-transition-new(root)',
      }
    );
  });
}

Ví dụ này sử dụng transition.ready, một lời hứa sẽ được giải quyết sau khi các phần tử giả lập chuyển đổi được tạo thành công. Các thuộc tính khác của đối tượng này được đề cập trong tài liệu tham khảo API.


Hiệu ứng chuyển đổi dưới dạng tính năng nâng cao

API Chuyển đổi thành phần hiển thị được thiết kế để "gói" một thay đổi DOM và tạo một chuyển đổi cho thay đổi đó. Tuy nhiên, quá trình chuyển đổi phải được coi là một tính năng nâng cao, tức là ứng dụng của bạn không được chuyển sang trạng thái "lỗi" nếu thay đổi DOM thành công nhưng quá trình chuyển đổi không thành công. Lý tưởng nhất là quá trình chuyển đổi không được xảy ra lỗi, nhưng nếu có lỗi thì không được làm gián đoạn trải nghiệm người dùng còn lại.

Để coi các hiệu ứng chuyển đổi là một tính năng nâng cao, hãy cẩn thận không sử dụng các lời hứa chuyển đổi theo cách khiến ứng dụng của bạn gửi nếu hiệu ứng chuyển đổi không thành công.

Không nên
async function switchView(data) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    await updateTheDOM(data);
    return;
  }

  const transition = document.startViewTransition(async () => {
    await updateTheDOM(data);
  });

  await transition.ready;

  document.documentElement.animate(
    {
      clipPath: [`inset(50%)`, `inset(0)`],
    },
    {
      duration: 500,
      easing: 'ease-in',
      pseudoElement: '::view-transition-new(root)',
    }
  );
}

Vấn đề với ví dụ này là switchView() sẽ từ chối nếu quá trình chuyển đổi không thể đạt đến trạng thái ready, nhưng điều đó không có nghĩa là thành phần hiển thị không chuyển đổi được. DOM có thể đã cập nhật thành công, nhưng có view-transition-name trùng lặp, vì vậy quá trình chuyển đổi đã bị bỏ qua.

Thay vào đó:

Nên
async function switchView(data) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    await updateTheDOM(data);
    return;
  }

  const transition = document.startViewTransition(async () => {
    await updateTheDOM(data);
  });

  animateFromMiddle(transition);

  await transition.updateCallbackDone;
}

async function animateFromMiddle(transition) {
  try {
    await transition.ready;

    document.documentElement.animate(
      {
        clipPath: [`inset(50%)`, `inset(0)`],
      },
      {
        duration: 500,
        easing: 'ease-in',
        pseudoElement: '::view-transition-new(root)',
      }
    );
  } catch (err) {
    // You might want to log this error, but it shouldn't break the app
  }
}

Ví dụ này sử dụng transition.updateCallbackDone để chờ cập nhật DOM và từ chối nếu không thành công. switchView không còn từ chối nếu quá trình chuyển đổi không thành công, mà sẽ phân giải khi quá trình cập nhật DOM hoàn tất và từ chối nếu quá trình này không thành công.

Nếu bạn muốn switchView phân giải khi thành phần hiển thị mới đã "định vị", tức là mọi hiệu ứng chuyển đổi ảnh động đã hoàn tất hoặc chuyển đến cuối, hãy thay thế transition.updateCallbackDone bằng transition.finished.


Không phải là polyfill, nhưng…

Đây không phải là một tính năng dễ dàng để polyfill. Tuy nhiên, hàm trợ giúp này giúp mọi việc trở nên dễ dàng hơn nhiều trong các trình duyệt không hỗ trợ hiệu ứng chuyển đổi khung hiển thị:

function transitionHelper({
  skipTransition = false,
  types = [],
  update,
}) {

  const unsupported = (error) => {
    const updateCallbackDone = Promise.resolve(update()).then(() => {});

    return {
      ready: Promise.reject(Error(error)),
      updateCallbackDone,
      finished: updateCallbackDone,
      skipTransition: () => {},
      types,
    };
  }

  if (skipTransition || !document.startViewTransition) {
    return unsupported('View Transitions are not supported in this browser');
  }

  try {
    const transition = document.startViewTransition({
      update,
      types,
    });

    return transition;
  } catch (e) {
    return unsupported('View Transitions with types are not supported in this browser');
  }
}

Bạn có thể sử dụng hàm này như sau:

function spaNavigate(data) {
  const types = isBackNavigation ? ['back-transition'] : [];

  const transition = transitionHelper({
    update() {
      updateTheDOMSomehow(data);
    },
    types,
  });

  // …
}

Trong các trình duyệt không hỗ trợ hiệu ứng chuyển đổi khung hiển thị, updateDOM vẫn sẽ được gọi nhưng sẽ không có hiệu ứng chuyển đổi dạng ảnh động.

Bạn cũng có thể cung cấp một số classNames để thêm vào <html> trong quá trình chuyển đổi, giúp bạn dễ dàng thay đổi quá trình chuyển đổi tuỳ thuộc vào loại điều hướng.

Bạn cũng có thể truyền true đến skipTransition nếu không muốn có ảnh động, ngay cả trong các trình duyệt hỗ trợ hiệu ứng chuyển đổi chế độ xem. Điều này rất hữu ích nếu trang web của bạn có lựa chọn ưu tiên của người dùng để tắt hiệu ứng chuyển đổi.


Làm việc với khung

Nếu bạn đang làm việc với một thư viện hoặc khung tóm tắt các thay đổi về DOM, thì phần khó khăn là biết thời điểm hoàn tất thay đổi về DOM. Dưới đây là một bộ ví dụ sử dụng trình trợ giúp ở trên trong nhiều khung.

  • React – khoá ở đây là flushSync, áp dụng đồng bộ một tập hợp các thay đổi trạng thái. Có, có một cảnh báo lớn về việc sử dụng API đó, nhưng Dan Abramov đảm bảo với tôi rằng việc này là phù hợp trong trường hợp này. Như thường lệ với React và mã không đồng bộ, khi sử dụng nhiều lời hứa do startViewTransition trả về, hãy đảm bảo rằng mã của bạn đang chạy ở trạng thái chính xác.
  • Vue.js – khoá ở đây là nextTick, thực hiện sau khi DOM được cập nhật.
  • Svelte – rất giống với Vue, nhưng phương thức để chờ thay đổi tiếp theo là tick.
  • Lit – điểm mấu chốt ở đây là lời hứa this.updateComplete trong các thành phần, lời hứa này sẽ thực hiện sau khi DOM được cập nhật.
  • Angular – khoá ở đây là applicationRef.tick, giúp xoá các thay đổi DOM đang chờ xử lý. Kể từ Angular phiên bản 17, bạn có thể sử dụng withViewTransitions đi kèm với @angular/router.

Tài liệu tham khảo API

const viewTransition = document.startViewTransition(update)

Bắt đầu một ViewTransition mới.

update là một hàm được gọi sau khi trạng thái hiện tại của tài liệu được ghi lại.

Sau đó, khi lời hứa do updateCallback trả về được thực hiện, quá trình chuyển đổi sẽ bắt đầu trong khung tiếp theo. Nếu lời hứa do updateCallback trả về bị từ chối, quá trình chuyển đổi sẽ bị bỏ qua.

const viewTransition = document.startViewTransition({ update, types })

Bắt đầu một ViewTransition mới với các loại đã chỉ định

update được gọi sau khi trạng thái hiện tại của tài liệu được chụp.

types đặt các loại đang hoạt động cho quá trình chuyển đổi khi chụp hoặc thực hiện quá trình chuyển đổi. Ban đầu, biến này trống. Hãy xem viewTransition.types ở phần bên dưới để biết thêm thông tin.

Thành phần của thực thể ViewTransition:

viewTransition.updateCallbackDone

Một lời hứa thực hiện khi lời hứa do updateCallback trả về thực hiện hoặc từ chối khi lời hứa đó từ chối.

API Chuyển đổi chế độ xem gói một thay đổi DOM và tạo một chuyển đổi. Tuy nhiên, đôi khi bạn không quan tâm đến việc ảnh động chuyển đổi có thành công hay không, bạn chỉ muốn biết liệu DOM có thay đổi hay không và khi nào DOM thay đổi. updateCallbackDone là dành cho trường hợp sử dụng đó.

viewTransition.ready

Một lời hứa sẽ thực hiện sau khi các phần tử giả lập cho hiệu ứng chuyển đổi được tạo và ảnh động sắp bắt đầu.

Phương thức này sẽ từ chối nếu không thể bắt đầu quá trình chuyển đổi. Điều này có thể là do cấu hình sai, chẳng hạn như view-transition-name trùng lặp hoặc nếu updateCallback trả về một lời hứa bị từ chối.

Điều này rất hữu ích khi tạo ảnh động cho các phần tử giả lập chuyển đổi bằng JavaScript.

viewTransition.finished

Một lời hứa thực hiện sau khi trạng thái kết thúc hiển thị đầy đủ và có thể tương tác với người dùng.

Phương thức này chỉ từ chối nếu updateCallback trả về một lời hứa bị từ chối, vì điều này cho biết trạng thái kết thúc không được tạo.

Nếu không, nếu quá trình chuyển đổi không bắt đầu hoặc bị bỏ qua trong quá trình chuyển đổi, thì trạng thái kết thúc vẫn được đạt đến, vì vậy finished sẽ thực hiện.

viewTransition.types

Đối tượng giống Set chứa các loại chuyển đổi khung hiển thị đang hoạt động. Để thao tác với các mục nhập, hãy sử dụng các phương thức thực thể clear(), add()delete().

Để phản hồi một loại cụ thể trong CSS, hãy sử dụng bộ chọn lớp giả :active-view-transition-type(type) trên gốc chuyển đổi.

Các loại sẽ tự động được dọn dẹp khi quá trình chuyển đổi chế độ xem kết thúc.

viewTransition.skipTransition()

Bỏ qua phần ảnh động của hiệu ứng chuyển đổi.

Thao tác này sẽ không bỏ qua việc gọi updateCallback, vì thay đổi DOM tách biệt với quá trình chuyển đổi.


Tài liệu tham khảo về kiểu và chuyển đổi mặc định

::view-transition
Phần tử giả gốc lấp đầy khung nhìn và chứa từng ::view-transition-group.
::view-transition-group

Có vị trí tuyệt đối.

Hiệu ứng chuyển đổi widthheight giữa trạng thái "trước" và "sau".

Chuyển đổi transform giữa tứ giác không gian khung nhìn "trước" và "sau".

::view-transition-image-pair

Được đặt chính xác để lấp đầy nhóm.

isolation: isolate để giới hạn hiệu ứng của mix-blend-mode trên thành phần hiển thị cũ và mới.

::view-transition-new::view-transition-old

Được đặt ở vị trí tuyệt đối ở trên cùng bên trái của trình bao bọc.

Lấp đầy 100% chiều rộng của nhóm, nhưng có chiều cao tự động, vì vậy, hình ảnh này sẽ duy trì tỷ lệ khung hình thay vì lấp đầy nhóm.

mix-blend-mode: plus-lighter để cho phép hiệu ứng chuyển đổi thực sự.

Chế độ xem cũ chuyển đổi từ opacity: 1 thành opacity: 0. Chế độ xem mới chuyển đổi từ opacity: 0 sang opacity: 1.


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. Để làm như vậy, 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.