ทำให้องค์ประกอบเคลื่อนไหวขณะเลื่อนด้วยภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน

ดูวิธีใช้ไทม์ไลน์การเลื่อนและไทม์ไลน์มุมมองเพื่อสร้างภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อนในลักษณะการประกาศ

เผยแพร่: 5 พฤษภาคม 2023

ภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน

Browser Support

  • Chrome: 115.
  • Edge: 115.
  • Firefox: behind a flag.
  • Safari: 26.

Source

ภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อนเป็นรูปแบบ UX ทั่วไปบนเว็บ ภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อนจะลิงก์กับตำแหน่งการเลื่อนของคอนเทนเนอร์ที่เลื่อนได้ ซึ่งหมายความว่าเมื่อคุณเลื่อนขึ้นหรือลง ภาพเคลื่อนไหวที่ลิงก์จะเลื่อนไปข้างหน้าหรือข้างหลังเพื่อตอบสนองโดยตรง ตัวอย่างเช่น เอฟเฟกต์ต่างๆ เช่น รูปภาพพื้นหลังแบบพารัลแลกซ์หรือตัวบ่งชี้การอ่านที่จะเลื่อนตามเมื่อคุณเลื่อน

ตัวบ่งชี้การอ่านที่ด้านบนของเอกสารซึ่งขับเคลื่อนด้วยการเลื่อน

ภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อนประเภทที่คล้ายกันคือภาพเคลื่อนไหวที่ลิงก์กับตำแหน่งขององค์ประกอบภายในคอนเทนเนอร์การเลื่อน เช่น องค์ประกอบจะค่อยๆ ปรากฏเมื่อเข้ามาในมุมมอง

รูปภาพในหน้านี้จะค่อยๆ ปรากฏขึ้นเมื่อเข้าสู่มุมมอง

วิธีคลาสสิกในการสร้างเอฟเฟกต์ประเภทนี้คือการตอบสนองต่อเหตุการณ์การเลื่อนในเทรดหลัก ซึ่งทำให้เกิดปัญหาหลัก 2 ประการ

  • เบราว์เซอร์สมัยใหม่จะเลื่อนในกระบวนการแยกต่างหาก จึงส่งเหตุการณ์การเลื่อนแบบไม่พร้อมกัน
  • ภาพเคลื่อนไหวในเทรดหลักอาจเกิดอาการกระตุก

ซึ่งทำให้การสร้างภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อนที่มีประสิทธิภาพซึ่งซิงค์กับการเลื่อนเป็นไปไม่ได้หรือทำได้ยากมาก

ตั้งแต่ Chrome เวอร์ชัน 115 เป็นต้นไป จะมีชุด API และแนวคิดใหม่ที่คุณใช้เพื่อเปิดใช้ภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อนแบบประกาศได้ ได้แก่ ไทม์ไลน์การเลื่อนและไทม์ไลน์มุมมอง

แนวคิดใหม่เหล่านี้ผสานรวมกับ Web Animations API (WAAPI) และ CSS Animations API ที่มีอยู่ ซึ่งช่วยให้แนวคิดใหม่เหล่านี้รับช่วงข้อดีที่ API ที่มีอยู่เหล่านี้มอบให้ได้ ซึ่งรวมถึงความสามารถในการเรียกใช้ภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อนนอกเทรดหลัก ใช่ คุณอ่านไม่ผิด ตอนนี้คุณสามารถมีภาพเคลื่อนไหวที่ราบรื่นสุดๆ ซึ่งขับเคลื่อนด้วยการเลื่อนและทำงานนอกเทรดหลักได้ด้วยโค้ดเพิ่มเติมเพียงไม่กี่บรรทัด มีอะไรที่ไม่ชอบบ้าง

ภาพเคลื่อนไหวบนเว็บ สรุปสั้นๆ

ภาพเคลื่อนไหวบนเว็บด้วย CSS

หากต้องการสร้างภาพเคลื่อนไหวใน CSS ให้กำหนดชุดคีย์เฟรมโดยใช้@keyframesกฎ @ ลิงก์กับองค์ประกอบโดยใช้พร็อพเพอร์ตี้ animation-name พร้อมทั้งตั้งค่า animation-duration เพื่อกำหนดระยะเวลาที่ควรใช้ในการเคลื่อนไหว นอกจากนี้ยังมีanimation-*พร็อพเพอร์ตี้แบบยาวอีกมากมายanimation-easing-functionและanimation-fill-modeเป็นเพียงตัวอย่างบางส่วน ซึ่งทั้งหมดนี้สามารถรวมกันในanimationรูปแบบย่อได้

ตัวอย่างเช่น นี่คือภาพเคลื่อนไหวที่ขยายองค์ประกอบบนแกน X พร้อมทั้งเปลี่ยนสีพื้นหลัง

@keyframes scale-up {
  from {
    background-color: red;
    transform: scaleX(0);
  }
  to {
    background-color: darkred;
    transform: scaleX(1);
  }
}

#progressbar {
  animation: 2.5s linear forwards scale-up;
}

ภาพเคลื่อนไหวบนเว็บด้วย JavaScript

ใน JavaScript คุณสามารถใช้ Web Animations API เพื่อให้ได้ผลลัพธ์เดียวกันได้ คุณทำได้โดยสร้างอินสแตนซ์ Animation และ KeyFrameEffect ใหม่ หรือใช้วิธี Element animate() ที่สั้นกว่ามาก

document.querySelector('#progressbar').animate(
  {
    backgroundColor: ['red', 'darkred'],
    transform: ['scaleX(0)', 'scaleX(1)'],
  },
  {
    duration: 2500,
    fill: 'forwards',
    easing: 'linear',
   }
);

ผลลัพธ์ภาพของข้อมูลโค้ด JavaScript ด้านบนจะเหมือนกับเวอร์ชัน CSS ก่อนหน้า

ไทม์ไลน์ภาพเคลื่อนไหว

โดยค่าเริ่มต้น ภาพเคลื่อนไหวที่แนบกับองค์ประกอบจะทำงานในไทม์ไลน์ของเอกสาร เวลาต้นทางจะเริ่มต้นที่ 0 เมื่อหน้าเว็บโหลด และจะเริ่มนับไปข้างหน้าเมื่อเวลาจริงผ่านไป นี่คือไทม์ไลน์ภาพเคลื่อนไหวเริ่มต้น และจนถึงตอนนี้ก็เป็นไทม์ไลน์ภาพเคลื่อนไหวเดียวที่คุณมีสิทธิ์เข้าถึง

ข้อกำหนดภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อนกำหนดไทม์ไลน์ประเภทใหม่ 2 ประเภทที่คุณใช้ได้ ดังนี้

  • ไทม์ไลน์ความคืบหน้าในการเลื่อน: ไทม์ไลน์ที่ลิงก์กับตำแหน่งการเลื่อนของคอนเทนเนอร์ที่เลื่อนได้ตามแกนใดแกนหนึ่ง
  • ดูไทม์ไลน์ความคืบหน้า: ไทม์ไลน์ที่ลิงก์กับตำแหน่งที่สัมพันธ์ขององค์ประกอบหนึ่งๆ ภายในคอนเทนเนอร์การเลื่อน

เลื่อนไทม์ไลน์ความคืบหน้า

ไทม์ไลน์ความคืบหน้าในการเลื่อนคือไทม์ไลน์ภาพเคลื่อนไหวที่ลิงก์กับความคืบหน้าในตำแหน่งการเลื่อนของคอนเทนเนอร์ที่เลื่อนได้ ซึ่งเรียกอีกอย่างว่า scrollport หรือ scroller ตามแกนใดแกนหนึ่ง โดยจะแปลงตำแหน่งในช่วงการเลื่อนเป็นเปอร์เซ็นต์ความคืบหน้า

ตำแหน่งการเลื่อนเริ่มต้นแสดงถึงความคืบหน้า 0% และตำแหน่งการเลื่อนสิ้นสุดแสดงถึงความคืบหน้า 100% ในภาพด้านล่าง คุณจะเห็นว่าความคืบหน้าจะเพิ่มขึ้นจาก 0% เป็น 100% เมื่อเลื่อนแถบเลื่อนจากบนลงล่าง

การแสดงภาพไทม์ไลน์ความคืบหน้าในการเลื่อน เมื่อเลื่อนลงไปที่ด้านล่างของแถบเลื่อน ค่าความคืบหน้าจะเพิ่มขึ้นจาก 0% เป็น 100%

✨ ลองด้วยตัวคุณเอง

ไทม์ไลน์ความคืบหน้าในการเลื่อนมักเรียกสั้นๆ ว่า "ไทม์ไลน์การเลื่อน"

ดูไทม์ไลน์ความคืบหน้า

ไทม์ไลน์ประเภทนี้จะลิงก์กับความคืบหน้าแบบสัมพัทธ์ขององค์ประกอบหนึ่งๆ ภายในคอนเทนเนอร์เลื่อน ระบบจะติดตามออฟเซ็ตการเลื่อนของผู้เลื่อนเช่นเดียวกับไทม์ไลน์ความคืบหน้าในการเลื่อน ซึ่งต่างจากไทม์ไลน์ความคืบหน้าในการเลื่อนตรงที่ตำแหน่งสัมพัทธ์ของออบเจ็กต์ภายในตัวเลื่อนนั้นจะเป็นตัวกำหนดความคืบหน้า

ซึ่งคล้ายกับวิธีที่ IntersectionObserver ทำงาน ซึ่งสามารถติดตามระดับการมองเห็นองค์ประกอบในแถบเลื่อน หากองค์ประกอบไม่ปรากฏในแถบเลื่อน แสดงว่าองค์ประกอบนั้นไม่ได้ตัดกัน หากมองเห็นภายในตัวเลื่อน แม้จะเห็นเพียงส่วนเล็กๆ ก็ถือว่ามีการตัดกัน

ไทม์ไลน์ความคืบหน้าในการดูจะเริ่มตั้งแต่ช่วงเวลาที่วัตถุเริ่มตัดกับแถบเลื่อนและสิ้นสุดเมื่อวัตถุหยุดตัดกับแถบเลื่อน ในภาพต่อไปนี้ คุณจะเห็นว่าความคืบหน้าจะเริ่มนับจาก 0% เมื่อออบเจ็กต์เข้าสู่คอนเทนเนอร์เลื่อน และจะถึง 100% ในขณะที่ออบเจ็กต์ออกจากคอนเทนเนอร์เลื่อน

การแสดงภาพไทม์ไลน์ความคืบหน้าในการดู ความคืบหน้าจะเพิ่มขึ้นจาก 0% เป็น 100% เมื่อวัตถุ (กรอบสีเขียว) เคลื่อนผ่านแถบเลื่อน

✨ ลองด้วยตัวคุณเอง

โดยปกติแล้วไทม์ไลน์ความคืบหน้าของมุมมองจะย่อเป็น "ไทม์ไลน์ของมุมมอง" คุณสามารถกำหนดเป้าหมายส่วนที่เฉพาะเจาะจงของไทม์ไลน์ของมุมมองตามขนาดของวัตถุได้ แต่จะอธิบายในภายหลัง

การใช้ไทม์ไลน์ความคืบหน้าในการเลื่อนอย่างมีประสิทธิภาพ

การสร้างไทม์ไลน์ความคืบหน้าในการเลื่อนแบบไม่ระบุชื่อใน CSS

วิธีที่ง่ายที่สุดในการสร้างไทม์ไลน์การเลื่อนใน CSS คือการใช้ฟังก์ชัน scroll() ซึ่งจะสร้างไทม์ไลน์การเลื่อนแบบไม่ระบุตัวตนที่คุณตั้งเป็นค่าสำหรับพร็อพเพอร์ตี้ animation-timeline ใหม่ได้

ตัวอย่าง

@keyframes animate-it {  }

.subject {
  animation: animate-it linear;
  animation-timeline: scroll(root block);
}

ฟังก์ชัน scroll() รับอาร์กิวเมนต์ <scroller> และ <axis>

ค่าที่ยอมรับสำหรับอาร์กิวเมนต์ <scroller> มีดังนี้

  • nearest: ใช้คอนเทนเนอร์เลื่อนระดับบนสุดที่ใกล้ที่สุด (ค่าเริ่มต้น)
  • root: ใช้ Viewport ของเอกสารเป็นคอนเทนเนอร์การเลื่อน
  • self: ใช้องค์ประกอบนั้นๆ เป็นคอนเทนเนอร์เลื่อน

ค่าที่ยอมรับสำหรับอาร์กิวเมนต์ <axis> มีดังนี้

  • block: ใช้การวัดความคืบหน้าตามแกนบล็อกของคอนเทนเนอร์เลื่อน (ค่าเริ่มต้น)
  • inline: ใช้การวัดความคืบหน้าตามแกนในบรรทัดของคอนเทนเนอร์เลื่อน
  • y: ใช้การวัดความคืบหน้าตามแกน Y ของคอนเทนเนอร์เลื่อน
  • x: ใช้การวัดความคืบหน้าตามแกน x ของคอนเทนเนอร์เลื่อน

เช่น หากต้องการเชื่อมโยงภาพเคลื่อนไหวกับตัวเลื่อนรูทในแกนบล็อก ค่าที่จะส่งไปยัง scroll() คือ root และ block เมื่อรวมกันแล้ว ค่าคือ scroll(root block)

การสาธิต: ตัวบ่งชี้ความคืบหน้าในการอ่าน

การสาธิตนี้มีตัวบ่งชี้ความคืบหน้าในการอ่านที่ตรึงไว้ที่ด้านบนของวิวพอร์ต ขณะที่คุณเลื่อนลงไปในหน้า แถบความคืบหน้าจะขยายขึ้นจนมีความกว้างเต็มวิวพอร์ตเมื่อไปถึงจุดสิ้นสุดของเอกสาร ระบบจะใช้ไทม์ไลน์ความคืบหน้าในการเลื่อนแบบไม่ระบุตัวตนเพื่อขับเคลื่อนภาพเคลื่อนไหว

สาธิต: ตัวบ่งชี้ความคืบหน้าในการอ่าน

✨ ลองด้วยตัวคุณเอง

ตัวบ่งชี้ความคืบหน้าในการอ่านจะอยู่ที่ด้านบนของหน้าโดยใช้ตำแหน่งคงที่ หากต้องการใช้ประโยชน์จากภาพเคลื่อนไหวแบบคอมโพสิต ไม่ใช่ width ที่เคลื่อนไหว แต่เป็นองค์ประกอบที่ลดขนาดลงในแกน X โดยใช้ transform

<body>
  <div id="progress"></div>
  …
</body>
@keyframes grow-progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

#progress {
  position: fixed;
  left: 0; top: 0;
  width: 100%; height: 1em;
  background: red;

  transform-origin: 0 50%;
  animation: grow-progress auto linear;
  animation-timeline: scroll();
}

ไทม์ไลน์สำหรับภาพเคลื่อนไหว grow-progress ในองค์ประกอบ #progress จะตั้งค่าเป็นไทม์ไลน์ที่ไม่ระบุชื่อซึ่งสร้างขึ้นโดยใช้ scroll() ไม่มีการส่งอาร์กิวเมนต์ไปยัง scroll() ดังนั้นระบบจะเปลี่ยนกลับไปใช้ค่าเริ่มต้น

ตัวเลื่อนเริ่มต้นที่จะติดตามคือตัวเลื่อน nearest และแกนเริ่มต้นคือ block ซึ่งจะกำหนดเป้าหมายไปยัง Scroller รูทได้อย่างมีประสิทธิภาพ เนื่องจากเป็น Scroller ที่ใกล้ที่สุดขององค์ประกอบ #progress ขณะติดตามทิศทางบล็อก

การสร้างไทม์ไลน์ความคืบหน้าในการเลื่อนที่มีชื่อใน CSS

อีกวิธีในการกำหนดไทม์ไลน์ความคืบหน้าในการเลื่อนคือการใช้ไทม์ไลน์ที่มีชื่อ ซึ่งอาจจะยาวกว่าเล็กน้อย แต่ก็มีประโยชน์ในกรณีที่คุณไม่ได้กำหนดเป้าหมายไปยังตัวเลื่อนหลักหรือตัวเลื่อนรูท หรือเมื่อหน้าเว็บใช้ไทม์ไลน์หลายรายการ หรือเมื่อการค้นหาอัตโนมัติไม่ทำงาน วิธีนี้จะช่วยให้คุณระบุไทม์ไลน์ความคืบหน้าในการเลื่อนได้ตามชื่อที่ตั้งไว้

หากต้องการสร้างไทม์ไลน์ความคืบหน้าในการเลื่อนที่มีชื่อในองค์ประกอบ ให้ตั้งค่าพร็อพเพอร์ตี้ CSS scroll-timeline-name ในคอนเทนเนอร์การเลื่อนเป็นตัวระบุที่คุณต้องการ ค่าต้องขึ้นต้นด้วย --

หากต้องการปรับแกนที่จะติดตาม ให้ประกาศพร็อพเพอร์ตี้ scroll-timeline-axis ด้วย ค่าที่อนุญาตจะเหมือนกับอาร์กิวเมนต์ <axis> ของ scroll()

สุดท้าย หากต้องการลิงก์ภาพเคลื่อนไหวกับไทม์ไลน์ความคืบหน้าในการเลื่อน ให้ตั้งค่าพร็อพเพอร์ตี้ animation-timeline ในองค์ประกอบที่ต้องเคลื่อนไหวให้มีค่าเดียวกับตัวระบุที่ใช้สำหรับ scroll-timeline-name

ตัวอย่างโค้ด

@keyframes animate-it {  }

.scroller {
  scroll-timeline-name: --my-scroller;
  scroll-timeline-axis: inline;
}

.scroller .subject {
  animation: animate-it linear;
  animation-timeline: --my-scroller;
}

หากต้องการ คุณสามารถรวม scroll-timeline-name และ scroll-timeline-axis ไว้ในรูปแบบย่อ scroll-timeline ได้ เช่น

scroll-timeline: --my-scroller inline;

การสาธิตนี้มีตัวบ่งชี้ขั้นตอนที่แสดงเหนือภาพสไลด์แต่ละรายการ เมื่อภาพสไลด์มีรูปภาพ 3 รูป แถบตัวบ่งชี้จะเริ่มต้นที่ความกว้าง 33% เพื่อระบุว่าคุณกำลังดูรูปภาพที่ 1 จาก 3 รูป เมื่อรูปภาพสุดท้ายปรากฏขึ้น (ซึ่งจะพิจารณาจากแถบเลื่อนที่เลื่อนไปจนสุด) ตัวบ่งชี้จะใช้ความกว้างทั้งหมดของแถบเลื่อน ใช้ไทม์ไลน์ความคืบหน้าในการเลื่อนที่มีชื่อเพื่อขับเคลื่อนภาพเคลื่อนไหว

สาธิต: ตัวบ่งชี้ขั้นตอนของภาพสไลด์แนวนอน

✨ ลองด้วยตัวคุณเอง

มาร์กอัปพื้นฐานสำหรับแกลเลอรีมีดังนี้

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

องค์ประกอบ .gallery__progress อยู่ในตำแหน่งที่แน่นอนภายในองค์ประกอบ Wrapper .gallery ขนาดเริ่มต้นจะกำหนดโดยพร็อพเพอร์ตี้ที่กำหนดเอง --num-images

.gallery {
  position: relative;
}


.gallery__progress {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 1em;
  transform: scaleX(calc(1 / var(--num-images)));
}

.gallery__scrollcontainer จะจัดวางองค์ประกอบ .gallery__entry ที่มีอยู่แนวนอนและเป็นองค์ประกอบที่เลื่อน .gallery__progress จะเคลื่อนไหวเมื่อติดตามตำแหน่งการเลื่อน ซึ่งทำได้โดยการอ้างอิงไทม์ไลน์ความคืบหน้าในการเลื่อนที่ชื่อว่า --gallery__scrollcontainer

@keyframes grow-progress {
  to { transform: scaleX(1); }
}

.gallery__scrollcontainer {
  overflow-x: scroll;
  scroll-timeline: --gallery__scrollcontainer inline;
}
.gallery__progress {
  animation: auto grow-progress linear forwards;
  animation-timeline: --gallery__scrollcontainer;
}

การสร้างไทม์ไลน์ความคืบหน้าในการเลื่อนด้วย JavaScript

หากต้องการสร้างไทม์ไลน์การเลื่อนใน JavaScript ให้สร้างอินสแตนซ์ใหม่ของคลาส ScrollTimeline ส่งกระเป๋าพร็อพเพอร์ตี้พร้อมด้วย source และ axis ที่ต้องการติดตาม

  • source: การอ้างอิงถึงองค์ประกอบที่มีแถบเลื่อนที่คุณต้องการติดตาม ใช้ document.documentElement เพื่อกำหนดเป้าหมายไปยังโปรแกรมเลื่อนรูท
  • axis: กำหนดแกนที่จะติดตาม ค่าที่ยอมรับจะเหมือนกับตัวแปร CSS คือ block, inline, x และ y
const tl = new ScrollTimeline({
  source: document.documentElement,
});

หากต้องการแนบกับภาพเคลื่อนไหวบนเว็บ ให้ส่งเป็นพร็อพเพอร์ตี้ timeline และละเว้น duration หากมี

$el.animate({
  opacity: [0, 1],
}, {
  timeline: tl,
});

การสาธิต: ตัวบ่งชี้ความคืบหน้าในการอ่าน (อีกครั้ง)

หากต้องการสร้างตัวบ่งชี้ความคืบหน้าในการอ่านด้วย JavaScript ขณะที่ใช้มาร์กอัปเดียวกัน ให้ใช้โค้ด JavaScript ต่อไปนี้

const $progressbar = document.querySelector('#progress');

$progressbar.style.transformOrigin = '0% 50%';
$progressbar.animate(
  {
    transform: ['scaleX(0)', 'scaleX(1)'],
  },
  {
    fill: 'forwards',
    timeline: new ScrollTimeline({
      source: document.documentElement,
    }),
  }
);

ผลลัพธ์ที่มองเห็นได้จะเหมือนกันในเวอร์ชัน CSS กล่าวคือ timelineจะติดตามตัวเลื่อนรูทและขยายขนาด #progress ในแกน x จาก 0% เป็น 100% ขณะที่คุณเลื่อนหน้าเว็บ

✨ ลองด้วยตัวคุณเอง

ฝีกใช้ไทม์ไลน์ความคืบหน้าในการดูให้ปฏิบัติได้จริง

การสร้างไทม์ไลน์ความคืบหน้าของมุมมองที่ไม่ระบุตัวตนใน CSS

หากต้องการสร้างไทม์ไลน์ความคืบหน้าในการดู ให้ใช้ฟังก์ชัน view() อาร์กิวเมนต์ที่ยอมรับคือ <axis> และ <view-timeline-inset>

  • <axis> เหมือนกับจากไทม์ไลน์ความคืบหน้าในการเลื่อน และกำหนดแกนที่จะติดตาม ค่าเริ่มต้นคือ block
  • <view-timeline-inset> ช่วยให้คุณระบุออฟเซ็ต(บวกหรือลบ) เพื่อปรับขอบเขตเมื่อระบบพิจารณาว่าองค์ประกอบอยู่ในมุมมองหรือไม่ ค่าต้องเป็นเปอร์เซ็นต์หรือ auto โดย auto เป็นค่าเริ่มต้น

เช่น หากต้องการเชื่อมโยงภาพเคลื่อนไหวกับองค์ประกอบที่ตัดกับตัวเลื่อนในแกนบล็อก ให้ใช้ view(block) คล้ายกับ scroll() ให้ตั้งค่านี้เป็นค่าสำหรับพร็อพเพอร์ตี้ animation-timeline และอย่าลืมตั้งค่า animation-duration เป็น auto

เมื่อใช้โค้ดต่อไปนี้ img ทุกรายการจะค่อยๆ ปรากฏขึ้นเมื่อเลื่อนผ่านวิวพอร์ต

@keyframes reveal {
  from { opacity: 0; }
  to { opacity: 1; }
}

img {
  animation: reveal linear;
  animation-timeline: view();
}

Intermezzo: ดูช่วงไทม์ไลน์

โดยค่าเริ่มต้น ภาพเคลื่อนไหวที่ลิงก์กับไทม์ไลน์ของมุมมองจะแนบกับช่วงไทม์ไลน์ทั้งหมด โดยจะเริ่มตั้งแต่ช่วงที่วัตถุกำลังจะเข้าสู่ Scrollport และสิ้นสุดเมื่อวัตถุออกจาก Scrollport ทั้งหมดแล้ว

นอกจากนี้ คุณยังลิงก์ไปยังส่วนที่เฉพาะเจาะจงของไทม์ไลน์การดูได้ด้วยการระบุช่วงที่ควรแนบ เช่น เมื่อวัตถุเข้าสู่แถบเลื่อนเท่านั้น ในภาพด้านล่าง ความคืบหน้าจะเริ่มนับจาก 0% เมื่อวัตถุเข้าสู่คอนเทนเนอร์เลื่อน แต่จะไปถึง 100% ตั้งแต่ช่วงเวลาที่วัตถุตัดกันทั้งหมด

ไทม์ไลน์ของมุมมองที่ตั้งค่าให้ติดตามช่วงรายการของเรื่อง โดยภาพเคลื่อนไหวจะทำงานขณะที่วัตถุกำลังเข้าสู่ Scrollport เท่านั้น

ช่วงไทม์ไลน์การดูที่เป็นไปได้ซึ่งคุณกำหนดเป้าหมายได้มีดังนี้

  • cover: แสดงช่วงทั้งหมดของไทม์ไลน์ความคืบหน้าในการดู
  • entry: แสดงช่วงเวลาที่ช่องหลักกำลังเข้าสู่ช่วงการมองเห็นความคืบหน้าในการดู
  • exit: แสดงช่วงเวลาที่ช่องหลักกำลังออกจากช่วงการมองเห็นความคืบหน้าในการดู
  • entry-crossing: แสดงช่วงเวลาที่กรอบหลักข้ามขอบเขตสิ้นสุด
  • exit-crossing: แสดงช่วงที่กรอบหลักข้ามขอบเขตเริ่มต้น
  • contain: แสดงช่วงที่กล่องหลักมีขอบเขตครอบคลุมทั้งหมด หรือครอบคลุมทั้งหมดในช่วงการมองเห็นความคืบหน้าในการดูภายใน Scrollport ซึ่งขึ้นอยู่กับว่าตัวแบบสูงกว่าหรือต่ำกว่าผู้เลื่อน

หากต้องการกำหนดช่วง คุณต้องตั้งค่า range-start และ range-end โดยแต่ละรายการประกอบด้วยชื่อช่วง (ดูรายการด้านบน) และออฟเซ็ตช่วงเพื่อกำหนดตำแหน่งภายในชื่อช่วงนั้น โดยปกติแล้วออฟเซ็ตช่วงจะเป็นเปอร์เซ็นต์ตั้งแต่ 0% ถึง 100% แต่คุณยังระบุความยาวคงที่ได้ด้วย เช่น 20em

ตัวอย่างเช่น หากต้องการเรียกใช้ภาพเคลื่อนไหวตั้งแต่ตอนที่วัตถุเข้ามา ให้เลือก entry 0% เป็นจุดเริ่มต้นของช่วง หากต้องการให้เสร็จสิ้นภายในเวลาที่ผู้เข้าร่วมเข้ามา ให้เลือก entry 100% เป็นค่าสำหรับช่วงสิ้นสุด

ใน CSS คุณตั้งค่านี้ได้โดยใช้พร็อพเพอร์ตี้ animation-range ตัวอย่าง

animation-range: entry 0% entry 100%;

ใน JavaScript ให้ใช้พร็อพเพอร์ตี้ rangeStart และ rangeEnd

$el.animate(
  keyframes,
  {
    timeline: tl,
    rangeStart: 'entry 0%',
    rangeEnd: 'entry 100%',
  }
);

ใช้เครื่องมือที่ฝังไว้ด้านล่างเพื่อดูว่าชื่อช่วงแต่ละชื่อหมายถึงอะไร และเปอร์เซ็นต์ส่งผลต่อตำแหน่งเริ่มต้นและตำแหน่งสิ้นสุดอย่างไร ลองตั้งค่าช่วงเริ่มต้นเป็น entry 0% และช่วงสิ้นสุดเป็น cover 50% จากนั้นลากแถบเลื่อนเพื่อดูผลลัพธ์ของภาพเคลื่อนไหว

เครื่องมือแสดงภาพช่วงไทม์ไลน์ที่ดูได้ ซึ่งมีอยู่ที่ https://goo.gle/view-timeline-range-tool

ดูการบันทึก

ขณะที่ลองใช้เครื่องมือช่วงไทม์ไลน์ของมุมมองนี้ คุณอาจสังเกตเห็นว่าช่วงบางช่วงสามารถกำหนดเป้าหมายได้โดยใช้ชุดค่าผสมชื่อช่วง + ออฟเซ็ตช่วง 2 แบบที่แตกต่างกัน เช่น entry 0%, entry-crossing 0% และ cover 0% ทั้งหมดกำหนดเป้าหมายไปยังพื้นที่เดียวกัน

เมื่อเป้าหมาย range-start และ range-end คือ range-name เดียวกันและครอบคลุมทั้งช่วงตั้งแต่ 0% ถึง 100% คุณจะย่อค่าให้เหลือเพียงชื่อช่วงได้ เช่น animation-range: entry 0% entry 100%; สามารถเขียนใหม่ให้สั้นลงได้เป็น animation-range: entry

การสาธิต: การเปิดเผยรูปภาพ

การสาธิตนี้จะค่อยๆ แสดงรูปภาพเมื่อเข้าสู่พอร์ตเลื่อน โดยใช้ไทม์ไลน์มุมมองที่ไม่ระบุตัวตน เราได้ปรับช่วงภาพเคลื่อนไหวเพื่อให้แต่ละภาพมีความทึบแสงเต็มที่เมื่อเลื่อนไปครึ่งทาง

การสาธิต: การเปิดเผยรูปภาพ

✨ ลองด้วยตัวคุณเอง

เอฟเฟกต์การขยายทำได้โดยใช้ clip-path ที่เคลื่อนไหว CSS ที่ใช้สำหรับเอฟเฟกต์นี้คือ

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

.revealing-image {
  animation: auto linear reveal both;
  animation-timeline: view();
  animation-range: entry 25% cover 50%;
}

การสร้างไทม์ไลน์ความคืบหน้าของมุมมองที่มีชื่อใน CSS

คุณยังสร้างไทม์ไลน์การดูที่มีชื่อได้ด้วยเช่นเดียวกับไทม์ไลน์การเลื่อน แทนที่จะใช้พร็อพเพอร์ตี้ scroll-timeline-* ให้ใช้ตัวแปรที่มีคำนำหน้า view-timeline- แทน ได้แก่ view-timeline-name และ view-timeline-axis

โดยจะใช้ค่าประเภทเดียวกันและใช้กฎเดียวกันในการค้นหาไทม์ไลน์ที่มีชื่อ

การสาธิต: การเปิดเผยรูปภาพอีกครั้ง

การปรับปรุงการสาธิตการแสดงรูปภาพจากก่อนหน้านี้ โค้ดที่แก้ไขแล้วจะมีลักษณะดังนี้

.revealing-image {
  view-timeline-name: --revealing-image;
  view-timeline-axis: block;

  animation: auto linear reveal both;
  animation-timeline: --revealing-image;
  animation-range: entry 25% cover 50%;
}

เมื่อใช้ view-timeline-name: revealing-image ระบบจะติดตามองค์ประกอบภายในตัวเลื่อนที่ใกล้ที่สุด จากนั้นระบบจะใช้ค่าเดียวกันเป็นค่าสำหรับพร็อพเพอร์ตี้ animation-timeline เอาต์พุตภาพจะเหมือนเดิมทุกประการ

✨ ลองด้วยตัวคุณเอง

การสร้างไทม์ไลน์ความคืบหน้าในการดูใน JavaScript

หากต้องการสร้างไทม์ไลน์การดูใน JavaScript ให้สร้างอินสแตนซ์ใหม่ของคลาส ViewTimeline ส่งกระเป๋าพร็อพเพอร์ตี้พร้อม subject ที่ต้องการติดตาม axis และ inset

  • subject: การอ้างอิงถึงองค์ประกอบที่คุณต้องการติดตามภายในแถบเลื่อนขององค์ประกอบเอง
  • axis: แกนที่จะติดตาม ค่าที่ยอมรับจะเหมือนกับตัวแปร CSS คือ block, inline, x และ y
  • inset: การปรับ(บวก)หรือ(ลบ)ของขอบเขตการเลื่อนเมื่อพิจารณาว่ากล่องอยู่ในมุมมองหรือไม่
const tl = new ViewTimeline({
  subject: document.getElementById('subject'),
});

หากต้องการแนบกับภาพเคลื่อนไหวบนเว็บ ให้ส่งเป็นพร็อพเพอร์ตี้ timeline และละเว้น duration หากมี คุณจะส่งข้อมูลช่วงโดยใช้พร็อพเพอร์ตี้ rangeStart และ rangeEnd ก็ได้

$el.animate({
  opacity: [0, 1],
}, {
  timeline: tl,
  rangeStart: 'entry 25%',
  rangeEnd: 'cover 50%',
});

✨ ลองด้วยตัวคุณเอง

สิ่งอื่นๆ ที่น่าลอง

แนบกับช่วงไทม์ไลน์ของมุมมองหลายช่วงด้วยคีย์เฟรม 1 ชุด

มาดูตัวอย่างรายชื่อติดต่อนี้กัน ซึ่งรายการในรายชื่อจะมีการเคลื่อนไหว เมื่อรายการในลิสต์เข้าสู่ Scrollport จากด้านล่าง รายการจะเลื่อน+จางลง และเมื่อออกจาก Scrollport ที่ด้านบน รายการจะเลื่อน+จางลง

การสาธิต: รายชื่อติดต่อ

✨ ลองด้วยตัวคุณเอง

สำหรับการสาธิตนี้ องค์ประกอบแต่ละรายการจะได้รับการตกแต่งด้วยไทม์ไลน์ของ View 1 รายการที่ติดตามองค์ประกอบเมื่อข้าม Scrollport แต่มีภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน 2 รายการแนบอยู่ animate-in จะเชื่อมโยงกับช่วง entry ของไทม์ไลน์ และanimate-out จะเชื่อมโยงกับช่วง exit ของไทม์ไลน์

@keyframes animate-in {
  0% { opacity: 0; transform: translateY(100%); }
  100% { opacity: 1; transform: translateY(0); }
}
@keyframes animate-out {
  0% { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-100%); }
}

#list-view li {
  animation: animate-in linear forwards,
             animate-out linear forwards;
  animation-timeline: view();
  animation-range: entry, exit;
}

นอกจากจะเรียกใช้ภาพเคลื่อนไหว 2 รายการที่ต่างกันซึ่งแนบมากับช่วง 2 ช่วงที่ต่างกันแล้ว คุณยังสร้างชุดคีย์เฟรมชุดเดียวที่มีข้อมูลช่วงอยู่แล้วได้ด้วย

@keyframes animate-in-and-out {
  entry 0%  {
    opacity: 0; transform: translateY(100%);
  }
  entry 100%  {
    opacity: 1; transform: translateY(0);
  }
  exit 0% {
    opacity: 1; transform: translateY(0);
  }
  exit 100% {
    opacity: 0; transform: translateY(-100%);
  }
}

#list-view li {
  animation: linear animate-in-and-out;
  animation-timeline: view();
}

เนื่องจากคีย์เฟรมมีข้อมูลช่วง คุณจึงไม่จำเป็นต้องระบุ animation-range ผลลัพธ์จะเหมือนกับก่อนหน้านี้ทุกประการ

✨ ลองด้วยตัวคุณเอง

การแนบกับไทม์ไลน์การเลื่อนที่ไม่ใช่บรรพบุรุษ

กลไกการค้นหาสำหรับไทม์ไลน์การเลื่อนและไทม์ไลน์มุมมองที่มีชื่อจะจำกัดไว้เฉพาะบรรพบุรุษของการเลื่อนเท่านั้น แต่บ่อยครั้งที่องค์ประกอบที่ต้องเคลื่อนไหวไม่ใช่องค์ประกอบย่อยของตัวเลื่อนที่ต้องติดตาม

พร็อพเพอร์ตี้ timeline-scope จะมีบทบาทในกรณีนี้ คุณใช้พร็อพเพอร์ตี้นี้เพื่อประกาศไทม์ไลน์ที่มีชื่อนั้นโดยไม่ต้องสร้างจริง ซึ่งจะทำให้ไทม์ไลน์ที่มีชื่อนั้นมีขอบเขตกว้างขึ้น ในทางปฏิบัติ คุณใช้พร็อพเพอร์ตี้ timeline-scope ในองค์ประกอบหลักที่แชร์เพื่อให้ไทม์ไลน์ของตัวเลื่อนย่อยแนบกับองค์ประกอบดังกล่าวได้

เช่น

.parent {
  timeline-scope: --tl;
}
.parent .scroller {
  scroll-timeline: --tl;
}
.parent .scroller ~ .subject {
  animation: animate linear;
  animation-timeline: --tl;
}

ในข้อมูลโค้ดนี้

  • องค์ประกอบ .parent ประกาศไทม์ไลน์ที่มีชื่อ --tl องค์ประกอบย่อยขององค์ประกอบนี้จะค้นหาและใช้เป็นค่าสำหรับพร็อพเพอร์ตี้ animation-timeline ได้
  • องค์ประกอบ .scroller จะกำหนดไทม์ไลน์การเลื่อนที่มีชื่อว่า --tl โดยค่าเริ่มต้น จะมีเฉพาะองค์กรย่อยเท่านั้นที่มองเห็น แต่เนื่องจาก .parent ตั้งค่าให้เป็น scroll-timeline-root จึงเชื่อมต่อกับองค์กรย่อย
  • องค์ประกอบ .subject ใช้ไทม์ไลน์ --tl โดยจะเดินขึ้นไปตามแผนผังบรรพบุรุษและค้นหา --tl ใน .parent เมื่อ --tl ใน .parent ชี้ไปยัง --tl ของ .scroller .subject จะติดตามไทม์ไลน์ความคืบหน้าในการเลื่อนของ .scroller โดยพื้นฐาน

กล่าวอีกนัยหนึ่งคือ คุณสามารถใช้ timeline-root เพื่อย้ายไทม์ไลน์ขึ้นไปยังบรรพบุรุษ (หรือที่เรียกว่าการยกระดับ) เพื่อให้ลูกหลานทั้งหมดของบรรพบุรุษเข้าถึงไทม์ไลน์ได้

พร็อพเพอร์ตี้ timeline-scope ใช้ได้กับทั้งไทม์ไลน์การเลื่อนและไทม์ไลน์การดู

การสาธิตและแหล่งข้อมูลเพิ่มเติม

การสาธิตทั้งหมดที่กล่าวถึงในบทความนี้อยู่ในมินิไซต์ scroll-driven-animations.style เว็บไซต์มีเดโมอื่นๆ อีกมากมายเพื่อเน้นย้ำถึงสิ่งที่ทำได้ด้วยภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อน

ตัวอย่างเพิ่มเติมอย่างหนึ่งคือรายการปกอัลบั้มนี้ โดยปกแต่ละปกจะหมุนในรูปแบบ 3 มิติเมื่อได้รับสปอตไลต์ตรงกลาง

การสาธิต: Cover Flow

✨ ลองด้วยตัวคุณเอง

หรือการสาธิตการซ้อนการ์ดนี้ที่ใช้ประโยชน์จาก position: sticky เมื่อมีการซ้อนการ์ด การ์ดที่ซ้อนอยู่แล้วจะเล็กลง ทำให้เกิดเอฟเฟกต์ความลึกที่สวยงาม สุดท้ายแล้ว สแต็กทั้งหมดจะเลื่อนออกไปจากมุมมองเป็นกลุ่ม

สาธิต: การซ้อนการ์ด

✨ ลองด้วยตัวคุณเอง

นอกจากนี้ scroll-driven-animations.style ยังมีชุดเครื่องมือต่างๆ เช่น การแสดงภาพความคืบหน้าของช่วงไทม์ไลน์ของมุมมอง ซึ่งรวมอยู่ในโพสต์นี้ก่อนหน้านี้

นอกจากนี้ มีอะไรใหม่ใน Web Animations ที่งาน Google I/O ’23 ยังครอบคลุมภาพเคลื่อนไหวที่ขับเคลื่อนด้วยการเลื่อนด้วย