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

[Tên người]
Joey Arhar

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 hoạt động tương tác khác. Tuy nhiên, có một vài khoảng trống trong ảnh động mượt mà trên nền tảng web. Những tính năng này bao gồm khả năng dễ dàng tạo hoạt ảnh nhập và thoát, cũng như tạo hoạt ảnh mượt mà đến và từ lớp trên cùng cho các phần tử có thể loại bỏ 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, cho phép các thuộc tính riêng biệt có ảnh động và hiệu ứng chuyển tiếp mượt mà.

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

  • Khả năng 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 có từ khoá allow-discrete để cho phép chuyển đổi các thuộc tính riêng biệt như display (Từ Chrome 117).
  • Quy tắc @starting-style để tạo ảnh động cho các hiệu ứng nhập từ display: none vào lớp trên cùng (Từ Chrome 117).
  • Thuộc tính overlay để kiểm soát hành vi của lớp trên cùng trong ảnh động (Từ Chrome 117). ## Hiển thị hoạt ảnh 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 khung hì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 để hỗ trợ việc này:

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

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

Ví dụ trước tạo hiệu ứng động cho độ mờ thành 0 trong khoảng thời gian 0,5 giây 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 cho ảnh độ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, quá trình chuyển đổi không thể tạo ả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à một ảnh động thoát. Trước tiên, thẻ sẽ quay trên trục y, chạy qua một chế độ xoay màu, sau đó tại 80% qua dòng thời gian, chuyển đổi độ mờ của nó từ 1 thành 0. Cuối cùng, thẻ này sẽ đổ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 chúng cho một phần tử, bạn có thể thiết lập điều kiện kích hoạt cho ảnh động. Ví dụ: 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 mà bạn muốn phát triể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 tiên.

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

Không giống như khi tạo ảnh động cho các thuộc tính riêng biệt 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 riêng biệt và là 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 cho thấy một kỹ thuật khác với bản minh hoạ ảnh động đầu tiên nhưng về mặt hình ảnh thì tương tự.

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 và sử dụng từ khoá allow-discrete ở cuối cách viết tắt transition cho mỗi 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 vào 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 nhập

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

Sử 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 nhập và 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ử đến và từ lớp trên cùng

Để tạo hiệu ứng động cho các phần tử đến và từ lớp trên cùng, hãy chỉ định @starting-style ở trạng thái "mở" để cho trình duyệt biết vị trí để 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]. Đối với cửa sổ bật lên, hãy sử dụng lớp giả lập :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. Truy cập bằng cách tạo ảnh động đi lên từ cuối khung nhìn, thoát hiệu ứng này lên phía trên cùng của khung nhìn. Mã này cũng được viết bằng CSS lồng ghép để đóng gói hình ảnh rõ ràng 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ả lập :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. Các đoạn mã thoát và biến đổi đối tượng cấp trên popoverdialog, đồ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, phần tử của bạn sẽ ngay lập tức quay lại ở dạng được cắt, biến đổi và che lại, đồng thời bạn sẽ không thấy quá trình chuyển đổi này xảy 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 đối tượng còn lại và đảm bảo đối tượng này nằm ở lớp trên cùng khi tạo ảnh động. Thao tá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 mở nhiều phần tử ở lớp trên cùng, lớp phủ sẽ giúp bạn kiểm soát việc chuyển đổi trong và ngoài lớp trên cùng một cách suôn sẻ. 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ẽ di 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 được mượt mà.

Lưu ý về hiệu ứng chuyển đổi thành phần hiển thị

Nếu bạn đang thay đổi 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 để có ảnh động mượt mà là chuyển đổi chế độ xem. Dưới đây là 2 ví dụ ở trên được tạo bằng cách sử dụng hiệu ứ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 phép biến đổi CSS khác, chuyển đổi khung hiển thị sẽ xử lý quá trình chuyển đổi. Chuyển đổi khung hiển thị được thiết lập như sau:

Trước tiên, trong CSS, hãy 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, hãy gói phần tử đột biến DOM (trong trường hợp này là xoá thẻ) trong hiệu ứng 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ý hiệu ứng làm mờ và biến đổi từng thẻ sang vị trí mới.

Một ví dụ khác về lợi ích của việc này là với 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 nhớ thêm một 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 trên nền tảng giúp chúng ta tiến gần hơn đến các hoạt ảnh nhập và thoát mượt mà trên nền tảng web. Để tìm hiểu thêm, hãy xem các đường liên kết sau: