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 do cuộn điều khiể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ẽ chạy tới hoặc lùi. Ngoài ra, với ảnh động do 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. Đ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 tự hiển thị khi xuất hiện trong khung hiển thị.

Tính năng mới trong Chrome 115 là hỗ trợ một 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 do cuộn khai báo. Các API mới này hoạt động cùng với các API Hoạt ảnh trên web và Hoạt ảnh 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 do cuộn bằng CSS. Khi hoàn thành 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 được giới thiệu trong tính năng thú vị này, 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 có hiệu ứng thị giác với 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 Tiến trình xem trong CSS.
  • Cách nhắm đến các loại phạm vi khác nhau của Dòng thời gian của thành phần hiển thị 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 đây (115 trở lên) trên ChromeOS, macOS hoặc Windows, trong đó cờ "Tính năng thử nghiệm của nền tảng web" được bật.
  • Có kiến thức cơ bản về HTML
  • Có kiến thức 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ó trong kho lưu trữ GitHub. Để bắt đầu, hãy nhân bản 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 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ề tiến trình ảnh động

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

Với ảnh động do cuộn điều khiển, bạn có thể sử dụng 2 loại tiến trình mới:

  • Dòng thời gian tiến trình cuộn
  • Xem tiến trình theo dòng thời gian

Trong CSS, bạn có thể đính kèm các tiến trình này vào ảnh động bằng thuộc tính animation-timeline. Hãy xem ý nghĩa của các tiến trình mới này và sự khác biệt giữa các tiến trình đó.

Dòng thời gian tiến trình cuộn

Dòng thời gian tiến trình cuộn là 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 thanh cuộn) dọc theo một trục cụ thể. Hàm này chuyển đổi một vị trí trong phạm vi cuộn thành tỷ lệ phần trăm tiến trình dọc theo dòng thời gian.

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

Xem tiến trình theo dòng thời gian

Loại tiến trình 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ư Tiến trình cuộn, độ lệch cuộn của thanh cuộn sẽ được theo dõi. Không giống như Tiến trình cuộn theo dòng thời gian, tiến trình này được xác định bằng vị trí tương đối của đối tượng trong thanh cuộn đó. Phương thức này tương đương với IntersectionObserver, theo dõi mức độ hiển thị của một phần tử trong thanh cuộn. Nếu phần tử không hiển thị trong thanh cuộn, thì phần tử đó không giao nhau. Nếu bạn có thể nhìn thấy phần lồng ghép bên trong thanh cuộn – ngay cả phần nhỏ nhất – thì đó là phần lồng ghép giao nhau.

Dòng thời gian tiến trình xem bắt đầu từ thời điểm đối tượng bắt đầu giao nhau với thanh cuộn và kết thúc khi đối tượng ngừng giao nhau với thanh cuộn. Trong hình ảnh trực quan sau, hãy lưu ý rằng tiến trình bắt đầu đếm từ 0% khi đối tượng vào vùng chứa cuộn và đạt đến 100% khi đối tượng 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ổng cuộn.

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

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

4. Tạo hiệu ứng nền có hiệu ứng thị giác 3D

Hiệu ứng đầu tiên cần thêm vào trang là hiệu ứng nền có hiệu ứng thị giác trên hình nền chính. Khi bạn cuộn xuống trang, 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 dựa vào Tiến trình cuộn theo dòng thời gian.

Để triển khai tính năng này, bạn cần thực hiện hai bước sau:

  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 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 đế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ử body:

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ử body. Thuộc tính animation-duration của nó được đặt thành một giây và sử dụng hiệu ứng làm dịu linear.

Cách dễ nhất để tạo tiến trình cuộn là sử dụng hàm scroll(). Thao tác này sẽ tạo 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 đối số <scroller><axis>.

Sau đây là các giá trị được chấp nhận cho đối số <scroller>:

  • nearest. Sử dụng vùng chứa cuộn gốc 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ử này làm vùng chứa cuộn.

Sau đây là các giá trị được chấp nhận cho đối số <axis>:

  • block. Sử dụng thước đ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 thước đo tiến trình dọc theo trục nội tuyến của vùng chứa cuộn.
  • y. Sử dụng thông tin đo lường tiến trình dọc theo trục y của vùng chứa cuộn.
  • x. Sử dụng thông tin đo lường 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 thanh 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 không có ý nghĩa, nên 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ì thanh cuộn gốc cũng là thanh cuộn mẹ gần nhất cho phần tử body, nên bạn cũng có thể sử dụng giá trị 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, nên bạn thậm chí có thể chọn bỏ qua các giá trị này. Trong trường hợp đó, bạn có thể đơn giản hoá mã như sau:

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 thứ diễn ra suôn sẻ, bạn sẽ có kết quả như sau:

Nếu không, 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 đ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 và có dạng như sau:

src/styles.css

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

Bạn cần đính kèm ảnh động này vào .Phần tử gallery__progress có Dòng thời gian tiến trình cuộn. Như đã trình bày ở bước trước, bạn có thể thực hiện việc này bằng cách tạo 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ù đoạn mã này có vẻ như sẽ hoạt động, nhưng thực tế là không vì cách hoạt động của các lượt tra cứu vùng chứa cuộn tự động bằng nearest. Khi tra cứu thanh cuộn gần nhất, phần tử này 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 vì phần tử này đã áp dụng position: relative. Điều này có nghĩa là phần tử .gallery__scrollcontainer (là thanh cuộn cần được nhắm mục tiêu) hoàn toàn 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 dòng thời gian đó bằng tên đó.

Để tạo Dòng thời gian tiến trình cuộn có tên trên một phần tử, hãy đặt thuộc tính CSS scroll-timeline-name trên vùng chứa cuộn thành một giá trị mà bạn muốn. Giá trị 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 đặt thuộc tính scroll-timeline-axis. 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 Tiến trình cuộn, hãy đặt thuộc tính animation-timeline trên phần tử cần tạo ảnh động thành cùng giá trị với giá trị nhận dạng được dùng cho scroll-timeline-name.

  • Thay đổi tệp styles.css để thêm nội dung sau:

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 thứ diễn ra suôn sẻ, bạn sẽ có kết quả như sau:

Nếu không, hãy xem nhánh solution-step-2 của mã.

6. Tạo ảnh động cho hình ảnh trong thư viện khi hình ảnh đó vào và thoát khỏi cuộn

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

Một hiệu ứng thú vị để thêm là làm mờ hình ảnh trong thư viện khi hình ảnh xuất hiện. Để làm việc này, bạn có thể sử dụng Dòng thời gian tiến trình xem.

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

  • <axis> giống với tiến trình cuộn trên Dòng thời gian và xác định trục cần theo dõi.
  • Với <view-timeline-inset>, bạn có thể chỉ định một độ dời (dương hoặc âm) để điều chỉnh các giới hạn khi một phần tử được coi là có trong khung 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 chính đó. Để thực hiện việc này, hãy tạo Dòng thời gian tiến trình xem trên mỗi 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 Dòng thời gian 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ó vẻ như có gì đó không ổn. Các phần tử này bắt đầu ở độ mờ 0 khi hoàn toàn không hiển thị và chỉ kết thúc ở độ mờ 1 khi hoàn toàn biến mất.

Đó là do phạm vi mặc định cho Tiến trình xem là phạm vi đầy đủ. Đây được gọi là dải ô cover.

  1. Để chỉ nhắm đến phạm vi entry của đối tượng, 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% (đối tượng sắp vào thanh cuộn) đến entry 100% (đối tượng đã vào hoàn toàn thanh cuộn).

Sau đây là các Phạm vi dòng thời gian của chế độ xem có thể có:

  • cover. Biểu thị toàn bộ phạm vi của tiến trình xem theo dòng thời gian.
  • entry. Biểu thị phạm vi trong đó hộp chính đang chuyển sang phạm vi hiển thị tiến trình xem.
  • exit. Biểu thị phạm vi trong đó hộp chính đang thoát khỏi phạm vi hiển thị tiến trình xem.
  • entry-crossing. Biểu thị phạm vi trong đó hộp chính vượt qua cạnh đường viền cuối.
  • exit-crossing. Biểu thị phạm vi trong đó hộp chính vượt qua cạnh đường viền bắt đầu.
  • contain. Biểu thị phạm vi trong đó hộp chính được chứa hoàn toàn hoặc bao phủ hoàn toàn phạm vi hiển thị tiến trình xem trong cổng cuộn. Điều này tuỳ thuộc vào việc đối tượng cao hơn hay thấp hơn thanh cuộn.

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

  1. Vì dải ô bắt đầu và kết thúc giống nhau ở đây và các độ dời 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:

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 thanh cuộn, bạn có thể làm tương tự như ảnh động trong nhưng nhắm đến 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 dải ô entry và các khung hình chính animate-out sẽ được áp dụng cho dải ô exit.

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

Nếu mọi thứ diễn ra suôn sẻ, bạn sẽ có kết quả như sau:

Nếu không, hãy xem nhánh solution-step-3 của mã.

7. Tạo ảnh động cho hình ảnh trong thư viện khi hình ảnh đó vào và ra khỏi cổng cuộn, bằng cách sử dụng một nhóm 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 nhóm khung hình chính đã chứa thông tin về dải ô.

Hình dạng của các 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 mờ 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 phạm vi xuất hiện trong các khung hình chính, bạn không cần chỉ định riêng animation-range nữa. Đính kèm các khung hình chính dưới dạng 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 thứ diễn ra suôn sẻ, bạn sẽ có kết quả giống như ở bước trước. Nếu không, 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à giờ đây đã biết cách tạo Dòng thời gian tiến trình cuộn và Dòng thời gian tiến trình xem trong CSS!

Tìm hiểu thêm

Tài nguyên: