Bốn tính năng CSS mới giúp hoạt ảnh nhập và thoát mượt mà

Chuyển động là một phần cốt lõi của mọi trải nghiệm kỹ thuật số, hướng dẫn người dùng từ tương tác này sang tương tác khác. Tuy nhiên, có một vài khoảng trống về ảnh động mượt mà trên nền tảng web. Các tính năng này bao gồm khả năng dễ dàng tạo ảnh động cho thao tác nhập và thoát, cũng như tạo hiệu ứng động cho và từ lớp trên cùng cho các phần tử có thể đóng như hộp thoại và cửa sổ bật lên.

Để lấp đầy những khoảng trống này, Chrome 116 và 117 cung cấp 4 tính năng mới trên nền tảng web, giúp tạo ảnh động và hiệu ứng chuyển đổi mượt mà cho các thuộc tính riêng biệt.

Bốn tính năng mới này bao gồm:

  • Có thể tạo ảnh động cho displaycontent-visibility trên dòng thời gian khung hình chính (Từ Chrome 116).
  • Thuộc tính transition-behavior với từ khoá allow-discrete để bật hiệu ứng chuyển đổi của các thuộc tính riêng biệt như display (Từ Chrome 117).
  • Quy tắc @starting-style để tạo hiệu ứng ảnh động cho mục nhập từ display: none và vào lớp trên cùng (Từ Chrome 117).
  • Thuộc tính overlay để kiểm soát hành vi lớp trên cùng trong ảnh động (Từ Chrome 117).

Hiển thị ảnh động trong khung hình chính

Từ Chrome 116, bạn có thể sử dụng displaycontent-visibility trong các quy tắc khung hình chính. Sau đó, các hình ảnh này sẽ hoán đổi tại thời điểm khung hình chính xuất hiện. Bạn không cần thêm giá trị mới nào để hỗ trợ định dạng này:

.card {
  animation: fade-out 0.5s forwards;
}

@keyframes fade-out {
  100% {
    opacity: 0;
    display: none;
  }
}

Ví dụ trước tạo ảnh động độ mờ thành 0 trong khoảng thời gian 0,5s và sau đó đặt hiển thị thành không có. Ngoài ra, từ khoá forwards đảm bảo ảnh động vẫn ở trạng thái kết thúc để phần tử được áp dụng vẫn là display: noneopacity: 0.

Đây là một ví dụ đơn giản bắt chước những việc bạn có thể làm với quá trình chuyển đổi (xem bản minh hoạ trong phần chuyển đổi). Tuy nhiên, hiệu ứng chuyển đổi không thể tạo các ảnh động phức tạp hơn, chẳng hạn như ví dụ sau:

.card {
  animation: spin-and-delete 1s ease-in forwards;
}

@keyframes spin-and-delete {
  0% {
    transform: rotateY(0);
    filter: hue-rotate(0);
  }
  80% {
    transform: rotateY(360deg);
    filter: hue-rotate(180deg);
    opacity: 1;
  }
  100% {
    opacity: 0;
    display: none;
  }
}

Ảnh động spin-and-delete là ảnh động thoát. Trước tiên, thẻ sẽ quay trên trục y, chạy theo chế độ xoay màu, sau đó tại 80% theo dòng thời gian, chuyển đổi độ mờ từ 1 sang 0. Cuối cùng, thẻ này sẽ được đổi từ display: block thành display: none.

Đối với các ảnh động thoát này, thay vì áp dụng trực tiếp cho một phần tử, bạn có thể thiết lập trình kích hoạt cho ảnh động. Chẳng hạn như bằng cách đính kèm trình nghe sự kiện vào nút kích hoạt một lớp để áp dụng ảnh động, như sau:

.spin-out {
   animation: spin-and-delete 1s ease-in forwards;
}
document.querySelector('.delete-btn').addEventListener('click', () => {
 document.querySelector('.card').classList.add('spin-out');
})

Ví dụ ở trên hiện có trạng thái kết thúc là display:none. Có nhiều trường hợp bạn sẽ muốn thực hiện thêm và xoá nút DOM với thời gian chờ để cho phép ảnh động hoàn tất trước.

Chuyển đổi ảnh động rời rạc

Không giống như khi tạo ảnh động cho các thuộc tính rời rạc bằng khung hình chính, để chuyển đổi các thuộc tính riêng biệt, bạn cần sử dụng chế độ hành vi chuyển đổi allow-discrete.

Thuộc tính transition-behavior

Chế độ allow-discrete cho phép chuyển đổi rời rạc và là một giá trị của thuộc tính transition-behavior. transition-behavior chấp nhận hai giá trị: normalallow-discrete.

.card {
  transition: opacity 0.25s, display 0.25s;
  transition-behavior: allow-discrete; /* Note: be sure to write this after the shorthand */
}

.card.fade-out {
  opacity: 0;
  display: none;
}
Lưu ý: Bản minh hoạ hiệu ứng chuyển đổi này minh hoạ một kỹ thuật khác với bản minh hoạ ảnh động đầu tiên, nhưng trông cũng tương tự như vậy.

Viết tắt transition cũng đặt giá trị này, vì vậy, bạn có thể bỏ qua thuộc tính này và sử dụng từ khoá allow-discrete ở cuối ký tự viết tắt transition cho mỗi hiệu ứng chuyển đổi.

.card {
  transition: opacity 0.5s, display 0.5s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

Nếu đang tạo ảnh động cho nhiều thuộc tính riêng biệt, bạn cần thêm allow-discrete sau mỗi thuộc tính mà bạn muốn tạo ảnh động. Ví dụ:

.card {
  transition: opacity 0.5s, display 0.5s allow-discrete, overlay 0.5s allow-discrete;
}

.card.fade-out {
  opacity: 0;
  display: none;
}

Quy tắc @starting-style cho ảnh động trong mục nhập

Cho đến nay, bài viết này đã đề cập đến ảnh động thoát, để tạo ảnh động cho mục nhập, bạn cần sử dụng quy tắc @starting-style.

Dùng @starting-style để áp dụng kiểu mà trình duyệt có thể tra cứu trước khi phần tử này mở trên trang. Đây là trạng thái "trước khi mở" (nơi bạn đang tạo ảnh động từ đó).

/*  0. BEFORE-OPEN STATE   */
/*  Starting point for the transition */
@starting-style {
  .item {
    opacity: 0;
    height: 0;
  }
}

/*  1. IS-OPEN STATE   */
/*  The state at which the element is open + transition logic */
.item {
  height: 3rem;
  display: grid;
  overflow: hidden;
  transition: opacity 0.5s, transform 0.5s, height 0.5s, display 0.5s allow-discrete;
}

/*  2. EXITING STATE   */
/*  While it is deleting, before DOM removal in JS, apply this
    transformation for height, opacity, and a transform which
    skews the element and moves it to the left before setting
    it to display: none */
.is-deleting {
  opacity: 0;
  height: 0;
  display: none;
  transform: skewX(50deg) translateX(-25vw);
}

Bây giờ, bạn có cả trạng thái mục nhập và trạng thái thoát cho các mục trong danh sách VIỆC CẦN LÀM sau:

Tạo ảnh động cho các phần tử từ lớp trên cùng đến lớp trên cùng

Để tạo ảnh động cho các phần tử ở lớp trên cùng và lớp trên cùng, hãy chỉ định @starting-style ở trạng thái "mở" để cho trình duyệt biết nơi bắt đầu tạo ảnh động. Đối với hộp thoại, trạng thái mở được xác định bằng thuộc tính [open]. Để sử dụng cửa sổ bật lên, hãy sử dụng lớp giả :popover-open.

Ví dụ đơn giản về hộp thoại có thể có dạng như sau:

/*   0. BEFORE-OPEN STATE   */
@starting-style {
  dialog[open] {
    translate: 0 100vh;
  }
}

/*   1. IS-OPEN STATE   */
dialog[open] {
  translate: 0 0;
}

/*   2. EXIT STATE   */
dialog {
  transition: translate 0.7s ease-out, overlay 0.7s ease-out allow-discrete, display 0.7s ease-out allow-discrete;
  translate: 0 100vh;
}

Trong ví dụ tiếp theo, hiệu ứng nhập và thoát là khác nhau. Nhập bằng cách tạo ảnh động từ cuối khung nhìn lên, thoát khỏi hiệu ứng đến đầu khung nhìn. Mã này cũng được viết với CSS lồng nhau để đóng gói hình ảnh hơn.

Khi tạo ảnh động cho một cửa sổ bật lên, hãy sử dụng lớp giả :popover-open thay vì thuộc tính open đã dùng trước đó.

.settings-popover {
  &:popover-open {
    /*  0. BEFORE-OPEN STATE  */
    /*  Initial state for what we're animating *in* from, 
        in this case: goes from lower (y + 20px) to center  */
    @starting-style {
      transform: translateY(20px);
      opacity: 0;
    }
    
    /*  1. IS-OPEN STATE  */
    /*  state when popover is open, BOTH:
        what we're transitioning *in* to 
        and transitioning *out* from */
    transform: translateY(0);
    opacity: 1;
  }
  
  /*  2. EXIT STATE  */
  /*  Initial state for what we're animating *out* to , 
      in this case: goes from center to (y - 50px) higher */
  transform: translateY(-50px);
  opacity: 0;
  
  /*  Enumerate transitioning properties, 
      including display and allow-discrete mode */
  transition: transform 0.5s, opacity 0.5s, display 0.5s allow-discrete;
}

overlay cơ sở lưu trú

Cuối cùng, để làm mờ popover hoặc dialog ở lớp trên cùng, hãy thêm thuộc tính overlay vào danh sách các hiệu ứng chuyển đổi. popoverdialog thoát đối tượng cấp trên cắt và biến đổi, đồng thời đặt nội dung vào lớp trên cùng. Nếu bạn không chuyển đổi overlay, thành phần của bạn sẽ ngay lập tức quay trở lại bị cắt bớt, biến đổi và che khuất, nên bạn sẽ không thấy hiệu ứng chuyển đổi này diễn ra.

[open] {
  transition: opacity 1s, display 1s allow-discrete;
}

Thay vào đó, hãy đưa overlay vào hiệu ứng chuyển đổi hoặc ảnh động để tạo ảnh động overlay cùng với các tính năng còn lại và đảm bảo thành phần này nằm ở lớp trên cùng khi tạo ảnh động. Việc này sẽ trông mượt mà hơn nhiều.

[open] {
  transition: opacity 1s, display 1s allow-discrete, overlay 1s allow-discrete;
}

Ngoài ra, khi bạn có nhiều phần tử mở trong lớp trên cùng, lớp phủ sẽ giúp bạn kiểm soát sự chuyển tiếp mượt mà vào và ra khỏi lớp trên cùng. Bạn có thể thấy sự khác biệt trong ví dụ đơn giản này. Nếu bạn không áp dụng overlay cho cửa sổ bật lên thứ hai khi chuyển đổi, thì trước tiên, cửa sổ này sẽ chuyển ra khỏi lớp trên cùng, nhảy ra sau cửa sổ bật lên khác trước khi bắt đầu chuyển đổi. Hiệu ứng này không mượt mà lắm.

Lưu ý về chuyển đổi chế độ xem

Nếu bạn đang thực hiện các thay đổi của DOM, chẳng hạn như thêm và xoá các phần tử khỏi DOM, thì một giải pháp tuyệt vời khác để ảnh động mượt mà là chuyển đổi chế độ xem. Dưới đây là hai trong số các ví dụ ở trên được tạo bằng cách sử dụng chuyển đổi khung hiển thị.

Trong bản minh hoạ đầu tiên này, thay vì thiết lập @starting-style và các biến đổi CSS khác, hiệu ứng chuyển đổi chế độ xem sẽ xử lý hiệu ứng chuyển đổi. Chuyển đổi chế độ xem được thiết lập như sau:

Trước tiên, trong CSS, hãy cung cấp cho mỗi thẻ một view-transition-name riêng.

.card-1 {
  view-transition-name: card-1;
}

.card-2 {
  view-transition-name: card-2;
}

/* etc. */

Sau đó, trong JavaScript, bao bọc hiện tượng đột biến DOM (trong trường hợp này là xóa thẻ), trong chuyển đổi chế độ xem.

deleteBtn.addEventListener('click', () => {
  // Check for browser support
  if (document.startViewTransition) {
    document.startViewTransition(() => {
      // DOM mutation
      card.remove();
    });
  } 
  // Alternative if no browser support
  else {
    card.remove();
  }
})

Giờ đây, trình duyệt có thể xử lý việc làm mờ và biến hình của mỗi thẻ sang vị trí mới.

Một ví dụ khác về việc tính năng này có thể hữu ích là bản minh hoạ thêm/xoá các mục trong danh sách. Trong trường hợp này, bạn cần phải thêm view-transition-name duy nhất cho mỗi thẻ được tạo.

Kết luận

Các tính năng mới này của nền tảng giúp chúng tôi tiến gần hơn đến việc nhập và thoát ảnh động mượt mà trên nền tảng web. Để tìm hiểu thêm, hãy truy cập vào các đường liên kết sau: