ฟีเจอร์ CSS ใหม่ 4 รายการสำหรับภาพเคลื่อนไหวเข้าและออกที่ราบรื่น

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

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

ฟีเจอร์ใหม่ 4 รายการต่อไปนี้

  • ความสามารถในการทำให้ display และ content-visibility เคลื่อนไหวบนไทม์ไลน์ของคีย์เฟรม (จาก Chrome 116)
  • พร็อพเพอร์ตี้ transition-behavior ที่มีคีย์เวิร์ด allow-discrete เพื่อเปิดใช้การเปลี่ยนพร็อพเพอร์ตี้แบบแยกกัน เช่น display (จาก Chrome 117)
  • กฎ @starting-style เพื่อให้เอฟเฟกต์รายการเคลื่อนไหวจาก display: none ไปยังเลเยอร์บนสุด (จาก Chrome 117)
  • พร็อพเพอร์ตี้ overlay สำหรับควบคุมลักษณะการทำงานของเลเยอร์บนระหว่างภาพเคลื่อนไหว (จาก Chrome 117)

แสดงภาพเคลื่อนไหวในคีย์เฟรม

จาก Chrome 116 คุณจะใช้ display และ content-visibility ในกฎคีย์เฟรมได้ จากนั้นจะสลับเมื่อคีย์เฟรมเกิดขึ้น ไม่จําเป็นต้องใช้ค่าใหม่เพิ่มเติมเพื่อรองรับฟีเจอร์นี้

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

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

ตัวอย่างก่อนหน้านี้ทำให้ความทึบแสงเป็น 0 ในช่วงเวลา 0.5 วินาที จากนั้นตั้งค่าการแสดงผลเป็น ไม่มี นอกจากนี้ คีย์เวิร์ด forwards ยังทำให้ภาพเคลื่อนไหวมีสถานะสิ้นสุดเพื่อให้องค์ประกอบที่ใช้กับองค์ประกอบดังกล่าวยังคงเป็น display: none และ opacity: 0

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

.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;
  }
}

ภาพเคลื่อนไหว spin-and-delete เป็นภาพเคลื่อนไหวเมื่อออก อันดับแรก การ์ดจะหมุนไปบนแกน Y, หมุนไปตามการหมุนโทนสี จากนั้นที่ 80% ไปตามไทม์ไลน์ ระบบจะเปลี่ยนความทึบแสงจาก 1 เป็น 0 ขั้นตอนสุดท้าย การสลับบัตรจาก display: block เป็น display: none

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

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

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

การเปลี่ยนภาพเคลื่อนไหวแบบไม่ต่อเนื่อง

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

พร็อพเพอร์ตี้ transition-behavior

โหมด allow-discrete คือสิ่งที่ทำให้คุณเปลี่ยนการใช้งานแบบไม่ต่อเนื่องได้ และเป็นค่าของพร็อพเพอร์ตี้ transition-behavior transition-behavior ยอมรับ 2 ค่า ได้แก่ normal และ allow-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;
}
หมายเหตุ: การสาธิตการเปลี่ยนนี้แสดงเทคนิคที่แตกต่างจากการสาธิตภาพเคลื่อนไหวครั้งแรก แต่มีลักษณะคล้ายกัน

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

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

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

หากกําลังทำให้พร็อพเพอร์ตี้ที่แยกกันหลายรายการเป็นภาพเคลื่อนไหว คุณจะต้องใส่ allow-discrete ไว้หลังพร็อพเพอร์ตี้แต่ละรายการที่ต้องการทำให้เคลื่อนไหว เช่น

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

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

กฎ @starting-style สำหรับรายการภาพเคลื่อนไหว

ถึงตอนนี้ บทความนี้ได้กล่าวถึงภาพเคลื่อนไหวเมื่อจบแล้ว หากต้องการสร้างภาพเคลื่อนไหวของรายการที่ต้องใช้กฎ @starting-style

ใช้ @starting-style เพื่อใช้สไตล์ที่เบราว์เซอร์ค้นหาได้ก่อนที่องค์ประกอบจะเปิดในหน้า ซึ่งเป็นสถานะ "ก่อนเปิด" (กรณีที่คุณเคลื่อนไหวเข้ามา)

/*  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);
}

ตอนนี้คุณมีทั้งสถานะเข้าและออกสำหรับรายการสิ่งที่ต้องทำต่อไปนี้

การทำให้องค์ประกอบเคลื่อนไหวไปยังและจากเลเยอร์ด้านบน

หากต้องการให้องค์ประกอบเคลื่อนไหวไปยังและจากเลเยอร์ด้านบน ให้ระบุ @starting-style ที่สถานะ "เปิด" เพื่อบอกเบราว์เซอร์ว่าจะเริ่มเคลื่อนไหวมาจากที่ใด สำหรับกล่องโต้ตอบ สถานะการเปิดจะกำหนดด้วยแอตทริบิวต์ [open] หากต้องการป๊อปอัป ให้ใช้คลาสจำลอง :popover-open

ตัวอย่างง่ายๆ ของกล่องโต้ตอบอาจมีลักษณะดังนี้

/*   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;
}

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

เมื่อทำให้ป๊อปอัปเคลื่อนไหว ให้ใช้คลาสสมมติ :popover-open แทนแอตทริบิวต์ open ที่ใช้ก่อนหน้านี้

.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 แห่ง

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

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

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

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

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

หมายเหตุเกี่ยวกับการเปลี่ยนมุมมอง

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

ในการสาธิตครั้งแรกนี้ แทนที่จะตั้งค่า @starting-style และการแปลง CSS อื่นๆ การเปลี่ยนการแสดงผลจะจัดการการเปลี่ยนแปลงแทนการตั้งค่า การเปลี่ยนมุมมองมีการตั้งค่าดังนี้

ก่อนอื่น ใน CSS ให้มอบ view-transition-name แก่การ์ดแต่ละใบ

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

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

/* etc. */

จากนั้นรวมการเปลี่ยนแปลง DOM (ในกรณีนี้คือการนำการ์ดออก) ใน JavaScript ในการเปลี่ยนผ่านมุมมอง

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

ตอนนี้เบราว์เซอร์สามารถจัดการการค่อยๆ จางลงและมอร์ฟของการ์ดแต่ละใบไปยังตำแหน่งใหม่ได้แล้ว

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

บทสรุป

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