Bắt đầu sử dụng ảnh động dựa trên cuộn trong CSS

1. Trước khi bắt đầu

Ảnh động dựa trên cuộn cho phép bạn kiểm soát việc phát ảnh động dựa trên vị trí cuộn của vùng chứa cuộn. Điều này có nghĩa là khi bạn cuộn lên hoặc xuống, ảnh động sẽ tua đi hoặc tua lại. Ngoài ra, với ảnh động dựa trên thao tác cuộn, bạn cũng có thể kiểm soát ảnh động dựa trên vị trí của một phần tử trong vùng chứa cuộn của nó. Điều này cho phép bạn tạo các hiệu ứng thú vị như hình nền thị sai, thanh tiến trình cuộn và hình ảnh hiển thị chính chúng khi chúng xuất hiện.

Tính năng mới trong Chrome 115 hỗ trợ tập hợp các lớp JavaScript và thuộc tính CSS, cho phép bạn dễ dàng tạo ảnh động dựa trên cuộn khai báo. Các API mới này hoạt động cùng với các API Ảnh động CSS và Ảnh động CSS hiện có.

Lớp học lập trình này hướng dẫn bạn cách tạo ảnh động dựa trên cuộn bằng cách sử dụng CSS. Sau khi hoàn tất lớp học lập trình này, bạn sẽ làm quen với nhiều thuộc tính CSS mới mà tính năng thú vị này giới thiệu, chẳng hạn như scroll-timeline, view-timeline, animation-timelineanimation-range.

Kiến thức bạn sẽ học được

  • Cách tạo hiệu ứng nền thị sai bằng Dòng thời gian cuộn trong CSS.
  • Cách tạo thanh tiến trình bằng Dòng thời gian cuộn trong CSS.
  • Cách tạo hiệu ứng hiển thị hình ảnh bằng chế độ xem dòng thời gian trong CSS.
  • Cách nhắm đến các loại phạm vi của một Chế độ xem dòng thời gian trong CSS.

Bạn cần có

Một trong những tổ hợp thiết bị sau:

  • Phiên bản Chrome mới (115 trở lên) trên ChromeOS, macOS hoặc Windows có "Các tính năng của nền tảng web thử nghiệm" cờ được đặt thành bật.
  • Hiểu biết cơ bản về HTML
  • Hiểu biết cơ bản về CSS, đặc biệt là ảnh động trong CSS

2. Bắt đầu thiết lập

Mọi thứ bạn cần cho dự án này đều có sẵn trong kho lưu trữ GitHub. Để bắt đầu, hãy sao chép mã và mở mã đó trong môi trường phát triển mà bạn yêu thích.

  1. Mở một thẻ trình duyệt mới rồi chuyển đến https://github.com/googlechromelabs/io23-scroll-driven-animations-codelab.
  2. Sao chép kho lưu trữ.
  3. Mở mã trong IDE mà bạn muốn sử dụng.
  4. Chạy npm install để cài đặt các phần phụ thuộc.
  5. Chạy npm start và truy cập vào http://localhost:3000/.
  6. Ngoài ra, nếu bạn chưa cài đặt npm, hãy mở tệp src/index.html trong Chrome.

3. Tìm hiểu về dòng thời gian của ảnh động

Theo mặc định, ảnh động được đính kèm với một phần tử sẽ chạy trên Dòng thời gian tài liệu. Điều đó có nghĩa là khi trang tải, ảnh động sẽ nhấp nháy theo thời gian. Đây là dòng thời gian ảnh động mặc định và cho đến bây giờ là dòng thời gian ảnh động duy nhất mà bạn có quyền truy cập.

Với ảnh động dựa trên cuộn, bạn có quyền truy cập vào hai loại dòng thời gian mới:

  • Tiến trình cuộn
  • Xem tiến trình

Trong CSS, bạn có thể đính kèm các dòng thời gian này vào ảnh động bằng thuộc tính animation-timeline. Cùng tìm hiểu ý nghĩa của các dòng thời gian mới này và chúng khác nhau như thế nào.

Tiến trình cuộn

Tiến trình cuộn là một dòng thời gian ảnh động được liên kết với tiến trình ở vị trí cuộn của vùng chứa cuộn (còn gọi là cuộn hoặc trình cuộn) dọc theo một trục cụ thể. Tính năng này chuyển đổi một vị trí trong dải ô cuộn thành một phần trăm tiến trình dọc theo dòng thời gian.

Vị trí cuộn bắt đầu biểu thị tiến trình 0% và vị trí cuộn kết thúc biểu thị tiến trình 100%. Trong hình minh hoạ sau đây, hãy lưu ý rằng tiến trình sẽ tăng từ 0% đến 100% khi bạn cuộn xuống.

Xem tiến trình

Loại dòng thời gian này được liên kết với tiến trình tương đối của một phần tử cụ thể trong vùng chứa cuộn. Giống như Dòng thời gian tiến trình cuộn, độ lệch cuộn của trình cuộn được theo dõi. Không giống như Tiến trình cuộn, vị trí tương đối của một chủ thể trong trình cuộn đó xác định tiến trình. Dữ liệu này tương đương với IntersectionObserver dùng để theo dõi mức độ hiển thị của một phần tử trong trình cuộn. Nếu phần tử không hiển thị trong trình cuộn thì phần tử này không giao nhau. Nếu phần này hiện bên trong thanh cuộn (ngay cả với phần nhỏ nhất) thì phần tử này đang giao nhau.

Tiến trình xem bắt đầu từ thời điểm một đối tượng bắt đầu giao với trình cuộn và kết thúc khi đối tượng ngừng giao với trình cuộn. Trong hình ảnh dưới đây, xin lưu ý rằng tiến trình bắt đầu đếm từ 0% khi đối tượng đi vào vùng chứa cuộn và đạt đến 100% tại thời điểm rời khỏi vùng chứa cuộn.

Theo mặc định, ảnh động được liên kết với Dòng thời gian tiến trình xem sẽ đính kèm vào toàn bộ phạm vi của ảnh động đó. Quá trình này bắt đầu từ thời điểm đối tượng vào cổng cuộn và kết thúc khi đối tượng rời khỏi cửa sổ cuộn.

Bạn cũng có thể liên kết báo cáo đó với một phần cụ thể của Tiến trình xem bằng cách chỉ định phạm vi cần đính kèm. Ví dụ: điều này có thể chỉ khi chủ thể đang vào trình cuộn. Trong hình ảnh sau đây, tiến trình bắt đầu được tính từ 0% khi đối tượng đi vào vùng chứa cuộn nhưng đã đạt đến 100% kể từ thời điểm đối tượng hoàn toàn giao cắt với nhau.

Phạm vi của Chế độ xem dòng thời gian mà bạn có thể nhắm mục tiêu là cover, contain, entry, exit, entry-crossingexit-crossing. Các dải ô này sẽ được giải thích ở phần sau trong lớp học lập trình này. Tuy nhiên, nếu bạn nóng lòng muốn biết, hãy sử dụng công cụ tại https://goo.gle/view-timeline-range-tool để xem nội dung của từng dải ô.

4. Tạo hiệu ứng nền thị sai

Hiệu ứng đầu tiên cần thêm vào trang là hiệu ứng nền thị sai trên hình nền chính. Khi bạn cuộn trang xuống, hình nền sẽ di chuyển, mặc dù tốc độ khác. Để làm được điều này, bạn cần sử dụng Dòng thời gian tiến trình cuộn.

Để triển khai việc này, có hai bước cần thực hiện:

  1. Tạo ảnh động di chuyển vị trí của hình nền.
  2. Liên kết ảnh động với tiến trình cuộn của tài liệu.

Tạo ảnh động

  1. Để tạo ảnh động, hãy sử dụng một tập hợp các khung hình chính thông thường. Trong đó, hãy di chuyển vị trí nền từ 0% theo chiều dọc lên 100%:

src/styles.css

@keyframes move-background {
  from {
    background-position: 50% 0%;
  }
  to {
    background-position: 50% 100%;
  }
}
  1. Bây giờ, hãy đính kèm các khung hình chính này vào phần tử nội dung:

src/styles.css

body {
  animation: 1s linear move-background;
}

Với mã này, ảnh động move-background sẽ được thêm vào phần tử nội dung. Thuộc tính animation-duration được đặt thành một giây và sử dụng tốc độ linear.

Cách dễ nhất để tạo dòng thời gian cho Tiến trình cuộn là sử dụng hàm scroll(). Thao tác này sẽ tạo ra một Dòng thời gian tiến trình cuộn ẩn danh mà bạn có thể đặt làm giá trị cho thuộc tính animation-timeline.

Hàm scroll() chấp nhận <scroller> và đối số <axis>.

Các giá trị được chấp nhận cho đối số <scroller> như sau:

  • nearest. Sử dụng vùng chứa cuộn cấp trên gần nhất (mặc định).
  • root. Sử dụng khung nhìn tài liệu làm vùng chứa cuộn.
  • self. Sử dụng chính phần tử đó làm vùng chứa cuộn.

Các giá trị được chấp nhận cho đối số <axis> như sau:

  • block. Sử dụng phép đo tiến trình dọc theo trục khối của vùng chứa cuộn (mặc định).
  • inline. Sử dụng phép đo tiến trình dọc theo trục cùng dòng của vùng chứa cuộn.
  • y. Sử dụng phép đo tiến trình dọc theo trục y của vùng chứa cuộn.
  • x. Sử dụng phép đo tiến trình dọc theo trục x của vùng chứa cuộn.

Để liên kết ảnh động với trình cuộn gốc trên trục khối, các giá trị cần truyền vào scroll()rootblock. Tổng hợp lại, giá trị là scroll(root block).

  • Đặt scroll(root block) làm giá trị cho thuộc tính animation-timeline trên phần nội dung.
  • Hơn nữa, vì animation-duration được biểu thị bằng giây là không hợp lý, hãy đặt thời lượng thành auto. Nếu bạn không chỉ định animation-duration, giá trị mặc định sẽ là auto.

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll(root block);
}

Vì trình cuộn gốc cũng là trình cuộn mẹ gần nhất đối với phần tử nội dung, nên bạn cũng có thể sử dụng giá trị của nearest:

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll(nearest block);
}

nearestblock là các giá trị mặc định, bạn thậm chí có thể chọn bỏ qua các giá trị này. Trong trường hợp đó, mã có thể được đơn giản hoá thành:

src/styles.css

body {
  animation: linear move-background;
  animation-duration: auto;
  animation-timeline: scroll();
}

Xác minh nội dung thay đổi

Nếu mọi việc đều đã tốt, giờ đây bạn sẽ có:

Nếu chưa, hãy xem nhánh solution-step-1 của mã.

5. Tạo thanh tiến trình cho thư viện hình ảnh

Trên trang này có một băng chuyền ngang cần có thanh tiến trình để cho biết bạn hiện đang xem ảnh nào.

Mã đánh dấu cho băng chuyền có dạng như sau:

src/index.html

<div class="gallery">
  <div class="gallery__scrollcontainer" style="--num-images: 3;">
    <div class="gallery__progress"></div>
    <div class="gallery__entry">
      ...
    </div>
    <div class="gallery__entry">
      ...
    </div>
    <div class="gallery__entry">
      ...
    </div>
  </div>
</div>

Các khung hình chính cho thanh tiến trình đã được đặt sẵn và có dạng như sau:

src/styles.css

@keyframes adjust-progress {
  from {
    transform: scaleX(calc(1 / var(--num-images)));
  }
  to {
    transform: scaleX(1);
  }
}

Hoạt ảnh này cần được đính kèm vào tệp .Phần tử gallery__progress có Dòng thời gian tiến trình cuộn. Như đã trình bày trong bước trước, bạn có thể làm được điều này bằng cách tạo một Dòng thời gian tiến trình cuộn ẩn danh bằng hàm scroll():

src/styles.css

.gallery__progress {
  animation: linear adjust-progress;
  animation-duration: auto;
  animation-timeline: scroll(nearest inline);
}

Mặc dù có vẻ như đoạn mã này vẫn hoạt động bình thường, nhưng không phải là do cách tự động tra cứu vùng chứa cuộn bằng nearest. Khi tìm trình cuộn gần nhất, phần tử sẽ chỉ xem xét những phần tử có thể ảnh hưởng đến vị trí của nó. Vì .gallery__progress được định vị tuyệt đối nên phần tử mẹ đầu tiên sẽ xác định vị trí của phần tử này là phần tử .gallery khi áp dụng position: relative. Điều này có nghĩa là phần tử .gallery__scrollcontainer (là trình cuộn cần nhắm mục tiêu) sẽ không được xem xét trong quá trình tra cứu tự động này.

Để giải quyết vấn đề này, hãy tạo một Dòng thời gian tiến trình cuộn có tên trên phần tử .gallery__scrollcontainer và liên kết .gallery__progress với phần tử này bằng tên đó.

Để tạo Dòng thời gian tiến trình cuộn được đặt tên trên một phần tử, hãy đặt thuộc tính CSS scroll-timeline-name trong vùng chứa cuộn thành một giá trị bạn muốn. Giá trị này phải bắt đầu bằng --.

Vì thư viện cuộn theo chiều ngang, nên bạn cũng cần thiết lập thuộc tính scroll-timeline-axis. Các giá trị được phép giống với đối số <axis> của scroll().

Cuối cùng, để liên kết ảnh động với Dòng thời gian tiến trình cuộn, hãy thiết lập thuộc tính animation-timeline trên phần tử cần tạo ảnh động thành giá trị giống với giá trị nhận dạng dùng cho scroll-timeline-name.

  • Hãy thay đổi tệp styles.css để đưa vào:

src/styles.css

.gallery__scrollcontainer {
  /* Create the gallery-is-scrolling timeline */
  scroll-timeline-name: --gallery-is-scrolling;
  scroll-timeline-axis: inline;
}

.gallery__progress {
  animation: linear adjust-progress;
  animation-duration: auto;
  /* Set gallery-is-scrolling as the timeline */
  animation-timeline: --gallery-is-scrolling;
}

Xác minh nội dung thay đổi

Nếu mọi việc đều đã tốt, giờ đây bạn sẽ có:

Nếu chưa, hãy xem nhánh solution-step-2 của mã.

6. Tạo ảnh động cho hình ảnh thư viện khi chúng vào và thoát khỏi cửa sổ cuộn

Thiết lập Dòng thời gian tiến trình xem ẩn danh

Một hiệu ứng hay mà bạn nên thêm là hiệu ứng mờ dần trong các hình ảnh trong thư viện khi chúng xuất hiện. Để làm được điều này, bạn có thể sử dụng Tiến trình xem.

Để tạo Tiến trình xem, bạn có thể sử dụng hàm view(). Các đối số được chấp nhận của lớp này là <axis><view-timeline-inset>.

  • <axis> giống với Tiến trình cuộn và xác định trục cần theo dõi.
  • Với <view-timeline-inset>, bạn có thể chỉ định độ lệch (dương hoặc âm) để điều chỉnh giới hạn khi một phần tử có được coi là đang hiển thị hay không.
  • Các khung hình chính đã có sẵn, vì vậy, bạn chỉ cần đính kèm các khung hình đó. Để thực hiện việc này, hãy tạo một Dòng thời gian tiến trình xem trên từng phần tử .gallery__entry.

src/styles.css

@keyframes animate-in {
  from {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
  to {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
}

.gallery__entry {
  animation: linear animate-in;
  animation-duration: auto;
  animation-timeline: view(inline);
}

Giới hạn phạm vi của Tiến trình xem

Nếu lưu CSS và tải trang, bạn sẽ thấy các phần tử mờ dần, nhưng có gì đó không giống. Các hình ảnh này bắt đầu ở độ mờ 0 khi chúng hoàn toàn nằm ngoài khung nhìn và chỉ kết thúc ở độ mờ 1 khi chúng đã thoát hoàn toàn.

Đó là vì phạm vi mặc định của Dòng thời gian tiến trình xem là toàn bộ. Đây được gọi là dải ô cover.

  1. Để chỉ nhắm đến phạm vi entry của tiêu đề, hãy sử dụng thuộc tính CSS animation-range để giới hạn thời điểm chạy ảnh động.

src/styles.css

.gallery__entry {
  animation: linear fade-in;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry 0% entry 100%;
}

Ảnh động hiện chạy từ entry 0% (chủ thể sắp vào trình cuộn) đến entry 100% (chủ thể đã nhập hoàn toàn vào trình cuộn).

Phạm vi dòng thời gian của chế độ xem có thể có như sau:

  • cover. Thể hiện toàn bộ tiến trình xem tiến trình.
  • entry. Biểu thị dải ô mà trong đó hộp chính sẽ nhập phạm vi chế độ hiển thị tiến trình xem.
  • exit. Biểu thị dải ô mà trong đó hộp chính thoát khỏi phạm vi chế độ hiển thị tiến trình xem.
  • entry-crossing. Đại diện cho dải ô trong đó hộp chính vượt qua cạnh đường viền cuối.
  • exit-crossing. Đại diện cho dải ô mà hộp chính vượt qua cạnh đường viền bắt đầu.
  • contain. Biểu thị dải ô mà trong đó hộp chính nằm hoàn toàn hoặc che khuất toàn bộ phạm vi hiển thị tiến trình khung hiển thị trong cửa sổ cuộn. Điều này phụ thuộc vào việc đối tượng cao hơn hay thấp hơn trình cuộn.

Sử dụng công cụ tại https://goo.gle/view-timeline-range-tool để xem từng phạm vi thể hiện nội dung gì và tỷ lệ phần trăm ảnh hưởng đến vị trí bắt đầu và kết thúc như thế nào.

  1. Vì dải ô bắt đầu và kết thúc ở đây giống nhau và độ lệch mặc định được sử dụng, hãy đơn giản hoá animation-range thành một tên dải ô ảnh động duy nhất:

src/styles.css

.gallery__entry {
  animation: linear animate-in;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry;
}
  • Để làm mờ hình ảnh khi chúng thoát khỏi trình cuộn, bạn có thể làm tương tự như ảnh động có hiệu ứng động nhưng nhắm mục tiêu một phạm vi khác.

src/styles.css

@keyframes animate-out {
  from {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
  to {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
}

.gallery__entry {
  animation: linear animate-in, linear animate-out;
  animation-duration: auto;
  animation-timeline: view(inline);
  animation-range: entry, exit;
}

Các khung hình chính animate-in sẽ được áp dụng cho phạm vi entry và các khung hình chính animate-out cho phạm vi exit.

Xác minh nội dung thay đổi

Nếu mọi việc đều đã tốt, giờ đây bạn sẽ có:

Nếu chưa, hãy xem nhánh solution-step-3 của mã.

7. Tạo ảnh động cho hình ảnh thư viện khi chúng vào và thoát khỏi cổng cuộn bằng một bộ khung hình chính

Trường hợp cho một nhóm khung hình chính

Thay vì đính kèm hai ảnh động vào các dải ô khác nhau, bạn có thể tạo một tập hợp các khung hình chính đã chứa thông tin dải ô.

Hình dạng của khung hình chính sẽ có dạng như sau:

@keyframes keyframes-name {
  range-name range-offset {
    ...
  }
  range-name range-offset {
    ...
  }
}
  1. Kết hợp các khung hình chính rõ dần và mờ dần như sau:

src/styles.css

@keyframes animate-in-and-out {
  entry 0% {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
  entry 90% {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }

  exit 10% {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
  exit 100% {
    opacity: 0;
    clip-path: inset(50% 0% 50% 0%);
  }
}
  1. Khi thông tin về phạm vi xuất hiện trong khung hình chính, bạn không cần chỉ định animation-range riêng biệt nữa. Đính kèm các khung hình chính làm thuộc tính animation.

src/styles.css

.gallery__entry {
  animation: linear animate-in-and-out both;
  animation-duration: auto;
  animation-timeline: view(inline);
}

Xác minh nội dung thay đổi

Nếu mọi việc đều thuận lợi, bạn sẽ nhận được kết quả tương tự như bước trước đó. Nếu chưa, hãy xem nhánh solution-step-4 của mã.

8. Xin chúc mừng!

Bạn đã hoàn thành lớp học lập trình này và bây giờ biết cách tạo Tiến trình cuộn và Xem tiến trình trong CSS!

Tìm hiểu thêm

Tài nguyên: