การเปลี่ยนมุมมองเอกสารเดียวกันสำหรับแอปพลิเคชันหน้าเว็บเดียว

เมื่อการเปลี่ยนมุมมองทำงานในเอกสารหนึ่ง การเปลี่ยนแปลงนั้นจะเรียกว่าการเปลี่ยนมุมมองเอกสารเดียวกัน โดยปกติจะเกิดในแอปพลิเคชันหน้าเว็บเดียว (SPA) ที่ใช้ JavaScript เพื่ออัปเดต DOM รองรับการเปลี่ยนมุมมองเอกสารเดียวกันใน Chrome ตั้งแต่ Chrome 111 เป็นต้นไป

หากต้องการทริกเกอร์การเปลี่ยนมุมมองเอกสารเดียวกัน ให้โทรหา document.startViewTransition

function handleClick(e) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    updateTheDOMSomehow();
    return;
  }

  // With a View Transition:
  document.startViewTransition(() => updateTheDOMSomehow());
}

เมื่อเรียกใช้ เบราว์เซอร์จะบันทึกสแนปชอตขององค์ประกอบทั้งหมดที่มีการประกาศพร็อพเพอร์ตี้ CSS เป็น view-transition-name โดยอัตโนมัติ

จากนั้นจะเรียกใช้ข้อมูลที่ผ่านใน Callback ซึ่งอัปเดต DOM ซึ่งระบบจะบันทึกสแนปชอตของสถานะใหม่หลังจากนั้น

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


การเปลี่ยนเริ่มต้น: ครอสเฟด

การเปลี่ยนมุมมองที่เป็นค่าเริ่มต้นจะเป็นการจางลง ซึ่งถือว่าเป็นการแนะนำ API ที่ดีดังนี้

function spaNavigate(data) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    updateTheDOMSomehow(data);
    return;
  }

  // With a transition:
  document.startViewTransition(() => updateTheDOMSomehow(data));
}

ตำแหน่งที่ updateTheDOMSomehow เปลี่ยน DOM เป็นสถานะใหม่ แต่คุณจะทำอย่างไรก็ได้ตามที่ต้องการ เช่น เพิ่มหรือนำองค์ประกอบออก เปลี่ยนชื่อคลาส หรือเปลี่ยนรูปแบบ

และเช่นนั้น หน้าจะค่อยๆ จางลง

การเฟดไขว้เริ่มต้น การสาธิตน้อยที่สุด แหล่งที่มา

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


การเปลี่ยนรุ่นเหล่านี้ทำงานอย่างไร

มาอัปเดตตัวอย่างโค้ดก่อนหน้านี้กัน

document.startViewTransition(() => updateTheDOMSomehow(data));

เมื่อมีการเรียก .startViewTransition() API จะบันทึกสถานะปัจจุบันของหน้า ซึ่งรวมถึงการถ่ายสแนปชอต

เมื่อดำเนินการเสร็จแล้ว ระบบจะเรียกใช้ Callback ที่ส่งไปยัง .startViewTransition() ซึ่งเป็นจุดที่ DOM มีการเปลี่ยนแปลง จากนั้น API จะบันทึกสถานะใหม่ของหน้าเว็บ

เมื่อบันทึกสถานะใหม่แล้ว API จะสร้างแผนผังองค์ประกอบสมมติขึ้นมาดังนี้

::view-transition
└─ ::view-transition-group(root)
   └─ ::view-transition-image-pair(root)
      ├─ ::view-transition-old(root)
      └─ ::view-transition-new(root)

::view-transition วางอยู่บนการวางซ้อนเหนือสิ่งอื่นทั้งหมดบนหน้าเว็บ ซึ่งจะเป็นประโยชน์หากคุณต้องการตั้งค่าสีพื้นหลังสำหรับการเปลี่ยน

::view-transition-old(root) คือภาพหน้าจอของมุมมองเดิม และ ::view-transition-new(root) คือตัวแทนของมุมมองใหม่แบบสด ทั้งการแสดงผลเป็น CSS "เนื้อหาที่แทนที่" (เช่น <img>)

มุมมองเดิมจะเคลื่อนไหวจาก opacity: 1 ไปยัง opacity: 0 ขณะที่มุมมองใหม่จะเคลื่อนไหวจาก opacity: 0 ไปยัง opacity: 1 ซึ่งทำให้เกิดการจางลง

ภาพเคลื่อนไหวทั้งหมดแสดงโดยใช้ภาพเคลื่อนไหว CSS เพื่อให้คุณปรับแต่งด้วย CSS ได้

ปรับแต่งการเปลี่ยน

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

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 5s;
}

เมื่อทำการเปลี่ยนแปลงครั้งนี้ การจางลงก็ช้าลงมาก

ครอสเฟดยาว การสาธิตน้อยที่สุด แหล่งที่มา

โอเค ยังไม่น่าประทับใจอยู่ดี แต่โค้ดต่อไปนี้จะใช้การเปลี่ยนแกนที่ใช้ร่วมกันของ Material Design แทน

@keyframes fade-in {
  from { opacity: 0; }
}

@keyframes fade-out {
  to { opacity: 0; }
}

@keyframes slide-from-right {
  from { transform: translateX(30px); }
}

@keyframes slide-to-left {
  to { transform: translateX(-30px); }
}

::view-transition-old(root) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}

::view-transition-new(root) {
  animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}

และผลลัพธ์ที่ได้มีดังนี้

การเปลี่ยนแกนที่แชร์ การสาธิตน้อยที่สุด แหล่งที่มา

การเปลี่ยนองค์ประกอบหลายรายการ

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

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

.main-header {
  view-transition-name: main-header;
}

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

และผลลัพธ์ที่ได้คือ

การเปลี่ยนของแกนที่ใช้ร่วมกันที่มีส่วนหัวคงที่ การสาธิตน้อยที่สุด แหล่งที่มา

ตอนนี้ส่วนหัวอยู่กับที่และจางลง

การประกาศ CSS ดังกล่าวทำให้โครงสร้างองค์ประกอบเทียมมีการเปลี่ยนแปลงดังนี้

::view-transition
├─ ::view-transition-group(root)
│  └─ ::view-transition-image-pair(root)
│     ├─ ::view-transition-old(root)
│     └─ ::view-transition-new(root)
└─ ::view-transition-group(main-header)
   └─ ::view-transition-image-pair(main-header)
      ├─ ::view-transition-old(main-header)
      └─ ::view-transition-new(main-header)

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

เอาล่ะ การเปลี่ยนที่เป็นค่าเริ่มต้นไม่ใช่แค่การจางลง แต่ ::view-transition-group ยังเปลี่ยนด้วย

  • ตำแหน่งและการเปลี่ยนรูปแบบ (โดยใช้ transform)
  • ความกว้าง
  • ส่วนสูง

ปัจจุบันนี้ยังคงไม่สำคัญแล้ว เนื่องจากส่วนหัวมีขนาดและตำแหน่งเดียวกันทั้ง 2 ด้านของการเปลี่ยนแปลง DOM แต่คุณยังสามารถดึงข้อความในส่วนหัวได้ โดยทำดังนี้

.main-header-text {
  view-transition-name: main-header-text;
  width: fit-content;
}

fit-content จะใช้เพื่อให้องค์ประกอบมีขนาดเท่ากับข้อความ แทนที่จะยืดออกเต็มความกว้างที่เหลืออยู่ หากไม่มีสิ่งนี้ ลูกศรย้อนกลับจะลดขนาดขององค์ประกอบข้อความส่วนหัวแทนที่จะเป็นขนาดเดียวกันในทั้ง 2 หน้า

ตอนนี้เรามี 3 ส่วนให้คุณเล่น ดังนี้

::view-transition
├─ ::view-transition-group(root)
│  └─ …
├─ ::view-transition-group(main-header)
│  └─ …
└─ ::view-transition-group(main-header-text)
   └─ …

อย่างไรก็ตาม ให้ใช้ค่าเริ่มต้น

ข้อความส่วนหัวแบบเลื่อน การสาธิตน้อยที่สุด แหล่งที่มา

ตอนนี้ข้อความส่วนหัวดูดีเล็กน้อยเพื่อให้มีพื้นที่สำหรับปุ่มย้อนกลับ


ทำให้องค์ประกอบเทียมหลายรายการเคลื่อนไหวด้วยวิธีเดียวกันด้วย view-transition-class

การรองรับเบราว์เซอร์

  • 125
  • 125
  • x
  • x

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

h1 {
    view-transition-name: title;
}
::view-transition-group(title) {
    animation-timing-function: ease-in-out;
}

#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
…
#card20 { view-transition-name: card20; }

::view-transition-group(card1),
::view-transition-group(card2),
::view-transition-group(card3),
::view-transition-group(card4),
…
::view-transition-group(card20) {
    animation-timing-function: var(--bounce);
}

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

คุณสามารถใช้ view-transition-class ในองค์ประกอบจำลองของการเปลี่ยนมุมมองเพื่อใช้กฎรูปแบบเดียวกัน

#card1 { view-transition-name: card1; }
#card2 { view-transition-name: card2; }
#card3 { view-transition-name: card3; }
#card4 { view-transition-name: card4; }
#card5 { view-transition-name: card5; }
…
#card20 { view-transition-name: card20; }

#cards-wrapper > div {
  view-transition-class: card;
}
html::view-transition-group(.card) {
  animation-timing-function: var(--bounce);
}

ตัวอย่างการ์ดต่อไปนี้ใช้ประโยชน์จากข้อมูลโค้ด CSS ก่อนหน้า การ์ดทั้งหมด รวมถึงการ์ดที่เพิ่มใหม่ จะใช้ช่วงเวลาเดียวกันด้วยตัวเลือก 1 รายการ คือ html::view-transition-group(.card)

การบันทึกการสาธิตการ์ด การใช้ view-transition-class จะมีผลกับ animation-timing-function เดียวกันกับบัตรทั้งหมดยกเว้นบัตรที่เพิ่มหรือนําออก

แก้ไขข้อบกพร่องการเปลี่ยน

เนื่องจากการเปลี่ยนมุมมองจะสร้างต่อยอดจากภาพเคลื่อนไหว CSS แผงภาพเคลื่อนไหวในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome จึงเหมาะสำหรับการแก้ไขข้อบกพร่องการเปลี่ยน

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

การเปลี่ยนมุมมองการแก้ไขข้อบกพร่องด้วยเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome

องค์ประกอบการเปลี่ยนไม่จำเป็นต้องเป็นองค์ประกอบ DOM เดียวกัน

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

ตัวอย่างเช่น คุณสามารถกำหนด view-transition-name สำหรับการฝังวิดีโอหลักได้ดังนี้

.full-embed {
  view-transition-name: full-embed;
}

จากนั้น เมื่อคลิกภาพขนาดย่อแล้ว ก็จะสามารถให้ view-transition-name เดิมได้ในช่วงการเปลี่ยนผ่านนี้:

thumbnail.onclick = async () => {
  thumbnail.style.viewTransitionName = 'full-embed';

  document.startViewTransition(() => {
    thumbnail.style.viewTransitionName = '';
    updateTheDOMSomehow();
  });
};

และผลลัพธ์ที่ได้

องค์ประกอบหนึ่งกำลังเปลี่ยนไปเป็นอีกองค์ประกอบหนึ่ง การสาธิตน้อยที่สุด แหล่งที่มา

ภาพปกจะเปลี่ยนไปเป็นภาพหลัก แม้ว่าจะเป็นองค์ประกอบที่แตกต่างกันในเชิงแนวคิด (และจริงๆ) แต่ API การเปลี่ยนจะปฏิบัติต่อองค์ประกอบเหล่านั้นเป็นสิ่งเดียวกันเนื่องจากแชร์ view-transition-name เดียวกัน

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


การเปลี่ยนการเข้าและออกที่กำหนดเอง

ดูตัวอย่างนี้

การเข้าและออกจากแถบด้านข้าง การสาธิตน้อยที่สุด แหล่งที่มา

แถบด้านข้างเป็นส่วนหนึ่งของการเปลี่ยนแปลงดังนี้

.sidebar {
  view-transition-name: sidebar;
}

แต่แถบด้านข้างต่างจากส่วนหัวในตัวอย่างก่อนหน้านี้ตรงที่จะไม่ปรากฏในทุกหน้า หากทั้ง 2 สถานะมีแถบด้านข้าง องค์ประกอบเทียมของการเปลี่ยนจะมีลักษณะดังนี้

::view-transition
├─ …other transition groups…
└─ ::view-transition-group(sidebar)
   └─ ::view-transition-image-pair(sidebar)
      ├─ ::view-transition-old(sidebar)
      └─ ::view-transition-new(sidebar)

อย่างไรก็ตาม หากแถบด้านข้างอยู่เฉพาะในหน้าใหม่ องค์ประกอบเทียม ::view-transition-old(sidebar) จะไม่ปรากฏ เนื่องจากไม่มีรูปภาพ "เก่า" ในแถบด้านข้าง การจับคู่รูปภาพจะมีเพียง ::view-transition-new(sidebar) ในทำนองเดียวกัน หากแถบด้านข้างอยู่เฉพาะในหน้าเก่า คู่รูปภาพจะมีเพียง ::view-transition-old(sidebar)

ในการสาธิตก่อนหน้านี้ แถบด้านข้างจะเปลี่ยนแตกต่างกันขึ้นอยู่กับว่ากำลังเข้า ออก หรืออยู่ในทั้ง 2 สถานะ เข้าโดยเลื่อนจากทางขวาและค่อยๆ ดังเข้า ออกด้วยการเลื่อนไปด้านขวาและค่อยๆ เลือนหายไป และจะอยู่กับที่เมื่อปรากฏในทั้ง 2 สถานะ

หากต้องการสร้างการเปลี่ยนให้เข้าและออกแบบเฉพาะเจาะจง คุณสามารถใช้ :only-child Pseudo-class เพื่อกำหนดเป้าหมายองค์ประกอบเทียมเก่าหรือใหม่เมื่อเป็นองค์ประกอบลูกเดียวในการจับคู่รูปภาพ

/* Entry transition */
::view-transition-new(sidebar):only-child {
  animation: 300ms cubic-bezier(0, 0, 0.2, 1) both fade-in,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}

/* Exit transition */
::view-transition-old(sidebar):only-child {
  animation: 150ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-right;
}

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

การอัปเดต DOM แบบไม่พร้อมกันและการรอเนื้อหา

การติดต่อกลับที่ส่งมายัง .startViewTransition() อาจให้ผลลัพธ์ตามที่สัญญาไว้ ซึ่งทำให้อัปเดต DOM แบบไม่พร้อมกันและรอให้เนื้อหาสำคัญพร้อมแสดง

document.startViewTransition(async () => {
  await something;
  await updateTheDOMSomehow();
  await somethingElse;
});

การเปลี่ยนจะยังไม่เริ่มต้นจนกว่าสัญญาจะบรรลุผล ในระหว่างนี้ หน้าเว็บค้าง จึงควรรักษาความล่าช้าให้น้อยที่สุด กล่าวอย่างเจาะจงคือ การดึงข้อมูลเครือข่ายควรดำเนินการก่อนเรียก .startViewTransition() ขณะที่หน้าเว็บยังคงโต้ตอบได้อย่างสมบูรณ์ ไม่ใช่แค่ทำเป็นส่วนหนึ่งของ Callback ของ .startViewTransition()

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

const wait = ms => new Promise(r => setTimeout(r, ms));

document.startViewTransition(async () => {
  updateTheDOMSomehow();

  // Pause for up to 100ms for fonts to be ready:
  await Promise.race([document.fonts.ready, wait(100)]);
});

อย่างไรก็ตาม ในบางกรณีคุณควรหลีกเลี่ยงความล่าช้าโดยสิ้นเชิงและใช้เนื้อหาที่มีอยู่แล้ว


ใช้ประโยชน์สูงสุดจากเนื้อหาที่มีอยู่แล้ว

ในกรณีที่ภาพขนาดย่อเปลี่ยนเป็นรูปภาพขนาดใหญ่ขึ้น ให้ทำดังนี้

ภาพขนาดย่อที่เปลี่ยนเป็นรูปภาพขนาดใหญ่ขึ้น ลองใช้เว็บไซต์เดโม

การเปลี่ยนภาพเริ่มต้นคือการค่อยๆ จางลง ซึ่งหมายความว่าภาพขนาดย่ออาจค่อยๆ ซีดจางกับภาพขนาดเต็มที่ยังไม่ได้โหลด

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

::view-transition-old(full-embed),
::view-transition-new(full-embed) {
  /* Prevent the default animation,
  so both views remain opacity:1 throughout the transition */
  animation: none;
  /* Use normal blending,
  so the new view sits on top and obscures the old view */
  mix-blend-mode: normal;
}

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

กรณีนี้จะไม่ทำงานหากมุมมองใหม่แสดงความโปร่งใส แต่ในกรณีนี้เราทราบว่าทำไม่ได้ เราจึงทำการเพิ่มประสิทธิภาพได้

จัดการกับการเปลี่ยนแปลงสัดส่วนภาพ

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

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

ในการเปลี่ยนแบบเริ่มต้น กลุ่มจะเคลื่อนไหวจากขนาดก่อนเป็นขนาดหลัง มุมมองเดิมและมุมมองใหม่จะมีความกว้างของกลุ่มเป็น 100% และมีความสูงอัตโนมัติ ซึ่งหมายความว่าจะคงอัตราส่วนกว้างยาวไว้โดยไม่คำนึงถึงขนาดของกลุ่ม

ตัวเลือกนี้เป็นค่าเริ่มต้นที่ดี แต่ไม่ใช่สิ่งที่ต้องการในกรณีนี้ ดังนั้น

::view-transition-old(full-embed),
::view-transition-new(full-embed) {
  /* Prevent the default animation,
  so both views remain opacity:1 throughout the transition */
  animation: none;
  /* Use normal blending,
  so the new view sits on top and obscures the old view */
  mix-blend-mode: normal;
  /* Make the height the same as the group,
  meaning the view size might not match its aspect-ratio. */
  height: 100%;
  /* Clip any overflow of the view */
  overflow: clip;
}

/* The old view is the thumbnail */
::view-transition-old(full-embed) {
  /* Maintain the aspect ratio of the view,
  by shrinking it to fit within the bounds of the element */
  object-fit: contain;
}

/* The new view is the full image */
::view-transition-new(full-embed) {
  /* Maintain the aspect ratio of the view,
  by growing it to cover the bounds of the element */
  object-fit: cover;
}

ซึ่งหมายความว่าภาพขนาดย่อจะอยู่ตรงกลางขององค์ประกอบเมื่อความกว้างขยายขึ้น แต่รูปภาพขนาดเต็มจะ "ยกเลิกการครอบตัด" เมื่อเปลี่ยนจาก 1:1 เป็น 16:9

โปรดดูข้อมูลเพิ่มเติมที่ดูการเปลี่ยน: การจัดการการเปลี่ยนแปลงสัดส่วนภาพ


ใช้คำค้นหาสื่อเพื่อเปลี่ยนการเปลี่ยนสำหรับสถานะอุปกรณ์ต่างๆ

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

องค์ประกอบหนึ่งกำลังเปลี่ยนไปเป็นอีกองค์ประกอบหนึ่ง การสาธิตน้อยที่สุด แหล่งที่มา

ซึ่งสามารถทำได้โดยใช้คิวรี่สื่อปกติ:

/* Transitions for mobile */
::view-transition-old(root) {
  animation: 300ms ease-out both full-slide-to-left;
}

::view-transition-new(root) {
  animation: 300ms ease-out both full-slide-from-right;
}

@media (min-width: 500px) {
  /* Overrides for larger displays.
  This is the shared axis transition from earlier in the article. */
  ::view-transition-old(root) {
    animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
      300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
  }

  ::view-transition-new(root) {
    animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
      300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
  }
}

อาจต้องเปลี่ยนองค์ประกอบที่กำหนด view-transition-name ด้วยโดยขึ้นอยู่กับคำค้นหาสื่อที่ตรงกัน


แสดงความรู้สึกต่อค่ากำหนด "การเคลื่อนไหวที่ลดลง"

ผู้ใช้จะระบุได้ว่าต้องการลดการเคลื่อนไหวผ่านระบบปฏิบัติการ และค่ากําหนดดังกล่าวคือแสดงใน CSS

คุณสามารถเลือกที่จะป้องกันไม่ให้มีการเปลี่ยนแปลงใดๆ สำหรับผู้ใช้เหล่านี้ได้ดังนี้

@media (prefers-reduced-motion) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

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


จัดการรูปแบบการเปลี่ยนมุมมองที่หลากหลายด้วยประเภทการเปลี่ยนมุมมอง

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

การบันทึกการสาธิตการใส่เลขหน้า ซึ่งจะใช้ทรานซิชันที่แตกต่างกันขึ้นอยู่กับหน้าที่คุณจะไปถึง

สำหรับกรณีนี้ คุณสามารถใช้ประเภทการเปลี่ยนมุมมอง ซึ่งช่วยให้คุณสามารถกำหนดประเภทการเปลี่ยนอย่างน้อย 1 ประเภทให้กับการเปลี่ยนในมุมมองแอ็กทีฟ ตัวอย่างเช่น เมื่อเปลี่ยนเป็นหน้าที่สูงกว่าในลำดับการแบ่งหน้า ให้ใช้ประเภท forwards และเมื่อไปยังหน้าที่ต่ำกว่า ให้ใช้ประเภท backwards ประเภทเหล่านี้จะใช้งานได้เมื่อจับภาพหรือทำการเปลี่ยนเท่านั้น และคุณสามารถปรับแต่งแต่ละประเภทผ่าน CSS ให้ใช้ภาพเคลื่อนไหวที่แตกต่างกันได้

หากต้องการใช้ประเภทในการเปลี่ยนมุมมองเอกสารเดียวกัน คุณจะต้องส่ง types ไปยังเมธอด startViewTransition หากต้องการดำเนินการดังกล่าว document.startViewTransition จะยอมรับออบเจ็กต์ด้วย โดย update เป็นฟังก์ชัน Callback ที่อัปเดต DOM และ types เป็นอาร์เรย์ที่มีประเภท

const direction = determineBackwardsOrForwards();

const t = document.startViewTransition({
  update: updateTheDOMSomehow,
  types: ['slide', direction],
});

หากต้องการตอบสนองต่อประเภทเหล่านี้ ให้ใช้ตัวเลือก :active-view-transition-type() ส่งผ่านtypeที่ต้องการกำหนดเป้าหมายลงในตัวเลือก วิธีนี้ช่วยให้คุณแยกสไตล์ของการเปลี่ยนมุมมองหลายรายการออกจากกันได้ โดยไม่ต้องประกาศของการเปลี่ยนแปลงหนึ่งซึ่งจะแทรกแซงการประกาศของอีกมุมมองหนึ่ง

เนื่องจากประเภทจะมีผลก็ต่อเมื่อบันทึกหรือดําเนินการเปลี่ยน คุณจึงใช้ตัวเลือกเพื่อตั้งค่าหรือไม่ได้ตั้งค่า view-transition-name ในองค์ประกอบสําหรับการเปลี่ยนมุมมองในประเภทดังกล่าวได้เท่านั้น

/* Determine what gets captured when the type is forwards or backwards */
html:active-view-transition-type(forwards, backwards) {
  :root {
    view-transition-name: none;
  }
  article {
    view-transition-name: content;
  }
  .pagination {
    view-transition-name: pagination;
  }
}

/* Animation styles for forwards type only */
html:active-view-transition-type(forwards) {
  &::view-transition-old(content) {
    animation-name: slide-out-to-left;
  }
  &::view-transition-new(content) {
    animation-name: slide-in-from-right;
  }
}

/* Animation styles for backwards type only */
html:active-view-transition-type(backwards) {
  &::view-transition-old(content) {
    animation-name: slide-out-to-right;
  }
  &::view-transition-new(content) {
    animation-name: slide-in-from-left;
  }
}

/* Animation styles for reload type only (using the default root snapshot) */
html:active-view-transition-type(reload) {
  &::view-transition-old(root) {
    animation-name: fade-out, scale-down;
  }
  &::view-transition-new(root) {
    animation-delay: 0.25s;
    animation-name: fade-in, scale-up;
  }
}

ในการสาธิตการใส่เลขหน้าต่อไปนี้ เนื้อหาของหน้าจะเลื่อนไปข้างหน้าหรือข้างหลังตามหมายเลขหน้าที่คุณกำลังนำทางไป ระบบจะกำหนดประเภทต่างๆ เมื่อมีการคลิกซึ่งส่งผ่านไปยัง document.startViewTransition

หากต้องการกำหนดเป้าหมายการเปลี่ยนผ่านมุมมองแอ็กทีฟ ไม่ว่าจะเป็นประเภทใดก็ตาม ให้ใช้ตัวเลือกคลาสเทียม :active-view-transition แทน

html:active-view-transition {
    …
}

จัดการรูปแบบการเปลี่ยนมุมมองหลายรูปแบบที่มีชื่อคลาสในรากการเปลี่ยนการแสดงผล

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

การเปลี่ยนโหมดต่างๆ เมื่อ "ย้อนกลับ" การสาธิตน้อยที่สุด แหล่งที่มา

ก่อนประเภทการเปลี่ยน วิธีจัดการกรณีเหล่านี้คือการตั้งชื่อคลาสในรูทการเปลี่ยนชั่วคราว เมื่อเรียก document.startViewTransition รากการเปลี่ยนนี้คือองค์ประกอบ <html> ซึ่งเข้าถึงได้โดยใช้ document.documentElement ใน JavaScript:

if (isBackNavigation) {
  document.documentElement.classList.add('back-transition');
}

const transition = document.startViewTransition(() =>
  updateTheDOMSomehow(data)
);

try {
  await transition.finished;
} finally {
  document.documentElement.classList.remove('back-transition');
}

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

ตอนนี้คุณใช้ชื่อคลาสดังกล่าวใน CSS เพื่อเปลี่ยนการเปลี่ยนผ่านได้แล้ว ดังนี้

/* 'Forward' transitions */
::view-transition-old(root) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}

::view-transition-new(root) {
  animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in, 300ms
      cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}

/* Overrides for 'back' transitions */
.back-transition::view-transition-old(root) {
  animation-name: fade-out, slide-to-right;
}

.back-transition::view-transition-new(root) {
  animation-name: fade-in, slide-from-left;
}

เมื่อใช้คิวรี่สื่อ การแสดงคลาสเหล่านี้ยังใช้เพื่อเปลี่ยนองค์ประกอบที่จะได้รับ view-transition-name ได้ด้วย


เรียกใช้การเปลี่ยนโดยไม่ตรึงภาพเคลื่อนไหวอื่นๆ

ลองดูการสาธิตการเปลี่ยนตำแหน่งวิดีโอ:

การเปลี่ยนวิดีโอ การสาธิตน้อยที่สุด แหล่งที่มา

คุณเห็นสิ่งผิดปกติไหม ไม่ต้องกังวลหากคุณไม่ได้ทำ ตรงนี้ช้าลงมาก

การเปลี่ยนวิดีโอทำงานช้าลง การสาธิตน้อยที่สุด แหล่งที่มา

ระหว่างการเปลี่ยนภาพ วิดีโอจะหยุดนิ่ง จากนั้นวิดีโอเวอร์ชันที่กำลังเล่นจะค่อยๆ ปรากฏขึ้น เนื่องจาก ::view-transition-old(video) เป็นภาพหน้าจอของมุมมองเก่า ขณะที่ ::view-transition-new(video) เป็นภาพสดของมุมมองใหม่

คุณแก้ไขปัญหานี้ได้ แต่ก่อนอื่นให้ถามตนเองว่าคุณควรแก้ไขไหม หากคุณไม่เห็น "ปัญหา" ในขณะที่การเปลี่ยนกำลังเล่นด้วยความเร็วปกติ ก็ไม่เป็นไร

หากคุณต้องการแก้ไขจริงๆ ไม่ต้องแสดง ::view-transition-old(video) สลับไปที่ ::view-transition-new(video) โดยตรง ซึ่งทําได้โดยลบล้างรูปแบบและภาพเคลื่อนไหวเริ่มต้น ดังนี้

::view-transition-old(video) {
  /* Don't show the frozen old view */
  display: none;
}

::view-transition-new(video) {
  /* Don't fade the new view in */
  animation: none;
}

เพียงเท่านี้ก็เรียบร้อยแล้ว

การเปลี่ยนวิดีโอทำงานช้าลง การสาธิตน้อยที่สุด แหล่งที่มา

ตอนนี้วิดีโอจะเล่นตลอดการเปลี่ยนหน้า


การสร้างภาพเคลื่อนไหวด้วย JavaScript

จนถึงตอนนี้ การเปลี่ยนทั้งหมดได้รับการกำหนดโดยใช้ CSS แต่บางครั้ง CSS ก็อาจไม่เพียงพอ

การเปลี่ยนแวดวง การสาธิตน้อยที่สุด แหล่งที่มา

การเปลี่ยนแปลง 2-3 อย่างต่อไปนี้ไม่สามารถทำได้หากใช้ CSS เพียงอย่างเดียว

  • ภาพเคลื่อนไหวจะเริ่มจากตำแหน่งที่คลิก
  • ภาพเคลื่อนไหวสิ้นสุดด้วยวงกลมที่มีรัศมีไปยังมุมที่ไกลที่สุด อย่างไรก็ตาม เราหวังว่าสิ่งนี้จะเป็นไปได้กับ CSS ในอนาคต

โชคดีที่คุณสามารถสร้างการเปลี่ยนภาพโดยใช้ Web Animation API ได้!

let lastClick;
addEventListener('click', event => (lastClick = event));

function spaNavigate(data) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    updateTheDOMSomehow(data);
    return;
  }

  // Get the click position, or fallback to the middle of the screen
  const x = lastClick?.clientX ?? innerWidth / 2;
  const y = lastClick?.clientY ?? innerHeight / 2;
  // Get the distance to the furthest corner
  const endRadius = Math.hypot(
    Math.max(x, innerWidth - x),
    Math.max(y, innerHeight - y)
  );

  // With a transition:
  const transition = document.startViewTransition(() => {
    updateTheDOMSomehow(data);
  });

  // Wait for the pseudo-elements to be created:
  transition.ready.then(() => {
    // Animate the root's new view
    document.documentElement.animate(
      {
        clipPath: [
          `circle(0 at ${x}px ${y}px)`,
          `circle(${endRadius}px at ${x}px ${y}px)`,
        ],
      },
      {
        duration: 500,
        easing: 'ease-in',
        // Specify which pseudo-element to animate
        pseudoElement: '::view-transition-new(root)',
      }
    );
  });
}

ตัวอย่างนี้ใช้ transition.ready ซึ่งเป็นสัญญาที่จะแก้ไขเมื่อสร้างองค์ประกอบจำลองของการเปลี่ยนสำเร็จแล้ว พร็อพเพอร์ตี้อื่นๆ ของออบเจ็กต์นี้ครอบคลุมอยู่ในเอกสารอ้างอิง API


การเปลี่ยนให้เป็นการเพิ่มประสิทธิภาพ

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

ในการทำให้การเปลี่ยนเป็นเหมือนการเพิ่มประสิทธิภาพ โปรดอย่าใช้สัญญาการเปลี่ยนในลักษณะที่จะทำให้แอปของคุณแสดงผลหากการเปลี่ยนไม่สำเร็จ

ไม่ควรทำ
async function switchView(data) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    await updateTheDOM(data);
    return;
  }

  const transition = document.startViewTransition(async () => {
    await updateTheDOM(data);
  });

  await transition.ready;

  document.documentElement.animate(
    {
      clipPath: [`inset(50%)`, `inset(0)`],
    },
    {
      duration: 500,
      easing: 'ease-in',
      pseudoElement: '::view-transition-new(root)',
    }
  );
}

ปัญหาในตัวอย่างนี้คือ switchView() จะปฏิเสธหากการเปลี่ยนเข้าถึงสถานะ ready ไม่ได้ แต่นั่นไม่ได้หมายความว่าจะเปลี่ยนมุมมองไม่สำเร็จ รายการ DOM อาจอัปเดตเรียบร้อยแล้ว แต่มี view-transition-name ที่ซ้ำกัน ระบบจึงข้ามการเปลี่ยน

ให้ดำเนินการต่อไปนี้แทน

ควรทำ
async function switchView(data) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    await updateTheDOM(data);
    return;
  }

  const transition = document.startViewTransition(async () => {
    await updateTheDOM(data);
  });

  animateFromMiddle(transition);

  await transition.updateCallbackDone;
}

async function animateFromMiddle(transition) {
  try {
    await transition.ready;

    document.documentElement.animate(
      {
        clipPath: [`inset(50%)`, `inset(0)`],
      },
      {
        duration: 500,
        easing: 'ease-in',
        pseudoElement: '::view-transition-new(root)',
      }
    );
  } catch (err) {
    // You might want to log this error, but it shouldn't break the app
  }
}

ตัวอย่างนี้ใช้ transition.updateCallbackDone เพื่อรอการอัปเดต DOM และปฏิเสธหากการอัปเดตไม่สำเร็จ switchView ไม่ปฏิเสธอีกต่อไปหากการเปลี่ยนล้มเหลว โดยจะได้รับการแก้ไขเมื่อการอัปเดต DOM เสร็จสมบูรณ์ และปฏิเสธหากการเปลี่ยนไม่สำเร็จ

หากคุณต้องการให้switchViewแก้ไขเมื่อมุมมองใหม่ "ตกลง" แล้ว เช่น เมื่อเปลี่ยนภาพเคลื่อนไหวเสร็จแล้วหรือข้ามไปที่ตอนท้าย ให้แทนที่ transition.updateCallbackDone ด้วย transition.finished


ไม่ใช่ Polyfill แต่...

ฟีเจอร์นี้ใช้กับ Polyfill ได้ไม่ง่ายนัก อย่างไรก็ตาม ฟังก์ชันตัวช่วยนี้ทำสิ่งต่างๆ ได้ง่ายขึ้นในเบราว์เซอร์ที่ไม่รองรับการเปลี่ยนมุมมอง ดังนี้

function transitionHelper({
  skipTransition = false,
  types = [],
  update,
}) {

  const unsupported = (error) => {
    const updateCallbackDone = Promise.resolve(update()).then(() => {});

    return {
      ready: Promise.reject(Error(error)),
      updateCallbackDone,
      finished: updateCallbackDone,
      skipTransition: () => {},
      types,
    };
  }

  if (skipTransition || !document.startViewTransition) {
    return unsupported('View Transitions are not supported in this browser');
  }

  try {
    const transition = document.startViewTransition({
      update,
      types,
    });

    return transition;
  } catch (e) {
    return unsupported('View Transitions with types are not supported in this browser');
  }
}

ซึ่งสามารถใช้ในลักษณะดังนี้

function spaNavigate(data) {
  const types = isBackNavigation ? ['back-transition'] : [];

  const transition = transitionHelper({
    update() {
      updateTheDOMSomehow(data);
    },
    types,
  });

  // …
}

ในเบราว์เซอร์ที่ไม่รองรับการเปลี่ยนมุมมอง ระบบจะยังคงเรียกใช้ updateDOM ต่อไป แต่จะไม่มีการเปลี่ยนแบบเคลื่อนไหว

นอกจากนี้ คุณยังระบุ classNames บางส่วนเพื่อเพิ่มลงใน <html> ระหว่างการเปลี่ยนได้ด้วย ซึ่งจะช่วยให้เปลี่ยนการเปลี่ยนโดยขึ้นอยู่กับประเภทการนำทางได้ง่ายขึ้น

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


การทำงานกับเฟรมเวิร์ก

หากคุณกำลังใช้งานไลบรารีหรือเฟรมเวิร์กที่ลบการเปลี่ยนแปลง DOM ออก ส่วนที่ยากคือการทราบว่าการเปลี่ยนแปลง DOM เสร็จสมบูรณ์เมื่อใด ต่อไปนี้เป็นตัวอย่างการใช้ผู้ช่วยด้านบนในเฟรมเวิร์กต่างๆ

  • ตอบสนอง - คีย์คือ flushSync ซึ่งจะนําการเปลี่ยนแปลงสถานะชุดหนึ่งไปใช้แบบพร้อมกัน ใช่ มีคำเตือนที่สำคัญเกี่ยวกับการใช้ API นั้น แต่ Dan Abramov มั่นใจว่าผมใช้อย่างเหมาะสมในกรณีนี้ เช่นเดียวกับโค้ด React และ async เมื่อคุณใช้การสัญญาต่างๆ ที่ startViewTransition แสดงผล ให้ใช้โค้ดว่าของคุณทำงานด้วยสถานะที่ถูกต้อง
  • Vue.js - คีย์ในที่นี้คือ nextTick ซึ่งจะตอบสนองเมื่ออัปเดต DOM แล้ว
  • Svelte - คล้ายกับ Vue มาก แต่วิธีการรอการเปลี่ยนแปลงครั้งต่อไปคือ tick
  • Lit - กุญแจสำคัญในที่นี้คือคำสัญญาของ this.updateComplete ภายในคอมโพเนนต์ ซึ่งจะตอบสนองเมื่ออัปเดต DOM แล้ว
  • Angular - คีย์คือ applicationRef.tick ซึ่งจะล้างการเปลี่ยนแปลง DOM ที่รอดำเนินการ ใน Angular เวอร์ชัน 17 คุณสามารถใช้ withViewTransitions ที่มาพร้อมกับ @angular/router

เอกสารอ้างอิง API

const viewTransition = document.startViewTransition(update)

เริ่ม ViewTransition ใหม่

update เป็นฟังก์ชันที่จะเรียกใช้เมื่อบันทึกสถานะปัจจุบันของเอกสาร

จากนั้นเมื่อเป็นไปตามคำสัญญาที่ updateCallback ส่งกลับมา การเปลี่ยนจะเริ่มในเฟรมถัดไป หากคำสัญญาที่ updateCallback แสดงผลไว้ถูกปฏิเสธ ระบบจะยกเลิกการเปลี่ยน

const viewTransition = document.startViewTransition({ update, types })

เริ่ม ViewTransition ใหม่ด้วยประเภทที่ระบุ

จะมีการเรียก update เมื่อบันทึกสถานะปัจจุบันของเอกสาร

types จะกำหนดประเภทที่ใช้งานอยู่สำหรับการเปลี่ยนเมื่อบันทึกหรือทำการเปลี่ยน ตอนแรกจะว่างเปล่า ดูข้อมูลเพิ่มเติมด้านล่าง viewTransition.types

สมาชิกอินสแตนซ์ของ ViewTransition:

viewTransition.updateCallbackDone

คำมั่นสัญญาที่เป็นจริงเมื่อคำสัญญาที่ updateCallback ส่งกลับมาให้ ซึ่งตรงกับคำมั่นสัญญาหรือปฏิเสธเมื่อคำสัญญานั้นปฏิเสธ

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

viewTransition.ready

คำมั่นสัญญาที่จะเติมเต็มเมื่อมีการสร้างองค์ประกอบเทียมสำหรับการเปลี่ยน และภาพเคลื่อนไหวกำลังจะเริ่มต้น

โดยจะปฏิเสธหากการเปลี่ยนไม่สามารถเริ่มต้นได้ ซึ่งอาจเกิดจากการกําหนดค่าผิดพลาด เช่น view-transition-name ซ้ำกัน หรือหาก updateCallback แสดงสัญญาที่ถูกปฏิเสธ

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

viewTransition.finished

คำสัญญาที่จะตอบสนองความต้องการของผู้ใช้เมื่อสถานะสิ้นสุดนั้นเสร็จสมบูรณ์และโต้ตอบกับผู้ใช้ได้

โดยจะปฏิเสธก็ต่อเมื่อ updateCallback แสดงผลสัญญาที่ถูกปฏิเสธเท่านั้น เนื่องจากมีการระบุว่าไม่มีการสร้างสถานะสิ้นสุด

มิเช่นนั้น หากการเปลี่ยนล้มเหลวหรือถูกข้ามระหว่างการเปลี่ยน ระบบจะยังคงถึงสถานะสิ้นสุดเพื่อให้ finished ดำเนินการ

viewTransition.types

ออบเจ็กต์คล้าย Set ที่เก็บประเภทของการเปลี่ยนมุมมองแอ็กทีฟ ในการจัดการรายการ ให้ใช้วิธีการของอินสแตนซ์ clear(), add() และ delete()

หากต้องการตอบสนองต่อประเภทที่เฉพาะเจาะจงใน CSS ให้ใช้ตัวเลือกคลาสเทียม :active-view-transition-type(type) ในรากการเปลี่ยน

ระบบจะล้างประเภทออกโดยอัตโนมัติเมื่อการเปลี่ยนมุมมองเสร็จสิ้น

viewTransition.skipTransition()

ข้ามส่วนภาพเคลื่อนไหวของการเปลี่ยนภาพ

การดำเนินการนี้จะไม่ข้ามการเรียกใช้ updateCallback เนื่องจากการเปลี่ยนแปลง DOM แยกต่างหากจากการเปลี่ยน


รูปแบบเริ่มต้นและการอ้างอิงการเปลี่ยน

::view-transition
องค์ประกอบเทียมระดับรูทที่เติมเต็มวิวพอร์ตและมี ::view-transition-group แต่ละรายการ
::view-transition-group

อยู่ในตําแหน่งที่แน่นอน

การเปลี่ยน width และ height ระหว่างสถานะ "ก่อน" และ "หลัง"

การเปลี่ยน transform ระหว่างสี่เหลี่ยม "ก่อน" กับ "หลัง" พื้นที่วิวพอร์ตและช่องว่าง

::view-transition-image-pair

วางตัวให้เต็มกลุ่มได้อย่างแท้จริง

มี isolation: isolate เพื่อจำกัดผลกระทบของ mix-blend-mode ต่อการแสดงผลทั้งเก่าและใหม่

::view-transition-newและ::view-transition-old

อยู่ในตำแหน่งด้านซ้ายบนของ Wrapper

แสดงเต็ม 100% ของความกว้างของกลุ่ม แต่มีความสูงอัตโนมัติ ดังนั้นจึงจะคงอัตราส่วนกว้างยาวไว้แทนการเติมกลุ่ม

มี mix-blend-mode: plus-lighter เพื่อให้สามารถใช้การครอสเฟดได้จริง

มุมมองเดิมจะเปลี่ยนจาก opacity: 1 เป็น opacity: 0 มุมมองใหม่จะเปลี่ยนจาก opacity: 0 เป็น opacity: 1


ความคิดเห็น

เรายินดีรับฟังความคิดเห็นของนักพัฒนาแอปเสมอ โดยแจ้งปัญหากับคณะทำงาน CSS บน GitHub พร้อมคำแนะนำและคำถาม ใส่คำนำหน้าปัญหาของคุณเกี่ยวกับ [css-view-transitions]

หากคุณพบข้อบกพร่อง ให้รายงานข้อบกพร่องของ Chromium แทน