เผยแพร่: 17 ส.ค. 2021, อัปเดตล่าสุด: 25 ก.ย. 2024
เมื่อทรานซิชันของมุมมองทำงานในเอกสารเดียว จะเรียกว่าทรานซิชันของมุมมองในเอกสารเดียวกัน โดยปกติจะเกิดในแอปพลิเคชันหน้าเว็บเดียว (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());
}
เมื่อเรียกใช้ เบราว์เซอร์จะจับภาพหน้าจอขององค์ประกอบทั้งหมดที่มีการประกาศพร็อพเพอร์ตี้ view-transition-name
CSS โดยอัตโนมัติ
จากนั้นจะเรียกใช้การเรียกกลับที่ส่งผ่านซึ่งอัปเดต DOM หลังจากนั้นจะจับภาพสถานะใหม่
จากนั้นสแนปชอตเหล่านี้จะจัดเรียงเป็นแผนผังขององค์ประกอบสมมติและภาพเคลื่อนไหวโดยใช้พลังของภาพเคลื่อนไหว CSS ภาพนิ่ง 2 คู่จากสถานะเก่าและใหม่จะเปลี่ยนจากตำแหน่งและขนาดเดิมไปยังตำแหน่งใหม่อย่างราบรื่น ขณะที่เนื้อหาของภาพนิ่งดังกล่าวจะค่อยๆ เปลี่ยนเป็นภาพใหม่ หรือจะใช้ CSS เพื่อปรับแต่งภาพเคลื่อนไหวก็ได้หากต้องการ
ทรานซิชันเริ่มต้น: เฟดเข้า
การเปลี่ยนฉากเริ่มต้นคือการเปลี่ยนภาพแบบ Cross-fade จึงเหมาะที่จะใช้เป็นข้อมูลเบื้องต้นเกี่ยวกับ 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)
ตอนนี้มีกลุ่มการเปลี่ยน 2 กลุ่ม ป้ายกำกับหนึ่งสำหรับส่วนหัวและอีกช่องสำหรับส่วนที่เหลือ คุณสามารถกําหนดเป้าหมายรายการเหล่านี้ด้วย 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
การรองรับเบราว์เซอร์
สมมติว่าคุณมีการเปลี่ยนมุมมองด้วยการ์ดจำนวนมาก แต่ยังมีชื่อในหน้าด้วย หากต้องการให้การ์ดทั้งหมดยกเว้นชื่อเคลื่อนไหว คุณต้องเขียนตัวเลือกที่กำหนดเป้าหมายไปยังการ์ดแต่ละใบ
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 ก่อนหน้า การ์ดทั้งหมด รวมถึงการ์ดที่เพิ่มใหม่จะใช้ช่วงเวลาเดียวกันด้วยตัวเลือกเดียว html::view-transition-group(.card)
แก้ไขข้อบกพร่องของทรานซิชัน
เนื่องจากทรานซิชันของมุมมองสร้างขึ้นจากภาพเคลื่อนไหว CSS แผงภาพเคลื่อนไหวในเครื่องมือสำหรับนักพัฒนาเว็บใน 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)
จะไม่ปรากฏ เนื่องจากไม่มี 'old' สำหรับแถบด้านข้าง คู่รูปภาพจะมี ::view-transition-new(sidebar)
เท่านั้น ในทำนองเดียวกัน หากแถบด้านข้างอยู่เฉพาะในหน้าเก่า คู่รูปภาพจะมีเพียง ::view-transition-old(sidebar)
ในการแสดงตัวอย่างก่อนหน้านี้ แถบด้านข้างจะเปลี่ยนสถานะแตกต่างกันไป ขึ้นอยู่กับว่ากำลังปรากฏ กำลังจะหายไป หรือปรากฏในทั้ง 2 สถานะ โดยไอคอนจะปรากฏขึ้นโดยเลื่อนจากด้านขวาและค่อย ๆ ปรากฏขึ้น แล้วจะหายไปโดยเลื่อนไปทางขวาและค่อย ๆ หายไป และไอคอนจะยังคงอยู่ในตำแหน่งเดิมเมื่อแสดงในทั้ง 2 สถานะ
หากต้องการสร้างทรานซิชันของช่วงเข้าและช่วงออกที่เฉพาะเจาะจง คุณสามารถใช้:only-child
คลาสจำลองเพื่อกำหนดเป้าหมายองค์ประกอบจำลองเก่าหรือใหม่เมื่อเป็นองค์ประกอบย่อยเพียงรายการเดียวในคู่รูปภาพ ดังนี้
/* 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 {
…
}
จัดการสไตล์การเปลี่ยนมุมมองหลายสไตล์ด้วยชื่อคลาสที่รูทการเปลี่ยนมุมมอง
บางครั้งการเปลี่ยนผ่านจากมุมมองประเภทหนึ่งไปเป็นอีกมุมมองหนึ่งควรจะมีการเปลี่ยนที่ปรับแต่งมาโดยเฉพาะ หรือ 'back' การนำทางควรจะแตกต่างจาก "ไปข้างหน้า" การนำทาง
ก่อนประเภทการเปลี่ยน วิธีจัดการกรณีเหล่านี้คือการตั้งชื่อคลาสในรูทการเปลี่ยนชั่วคราว เมื่อเรียกใช้ 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;
}
เช่นเดียวกับ Media Query การมีคลาสเหล่านี้อาจใช้เพื่อเปลี่ยนองค์ประกอบที่จะได้ 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;
}
เพียงเท่านี้ก็เรียบร้อยแล้ว
ตอนนี้วิดีโอจะเล่นตลอดช่วงการเปลี่ยนภาพ
การรวมเข้ากับ API การนำทาง (และเฟรมเวิร์กอื่นๆ)
การเปลี่ยนมุมมองจะระบุในลักษณะที่ผสานรวมกับเฟรมเวิร์กหรือไลบรารีอื่นๆ ได้ ตัวอย่างเช่น หากแอปพลิเคชันหน้าเว็บเดียว (SPA) ของคุณใช้เราเตอร์ คุณสามารถปรับกลไกการอัปเดตของเราเตอร์เพื่ออัปเดตเนื้อหาโดยใช้การเปลี่ยนมุมมอง
ในข้อมูลโค้ดต่อไปนี้ซึ่งได้มาจากการสาธิตการใส่เลขหน้านี้ เครื่องจัดการการสกัดกั้นของ API การนำทางจะมีการปรับให้เรียกใช้ document.startViewTransition
เมื่อรองรับการเปลี่ยนมุมมอง
navigation.addEventListener("navigate", (e) => {
// Don't intercept if not needed
if (shouldNotIntercept(e)) return;
// Intercept the navigation
e.intercept({
handler: async () => {
// Fetch the new content
const newContent = await fetchNewContent(e.destination.url, {
signal: e.signal,
});
// The UA does not support View Transitions, or the UA
// already provided a Visual Transition by itself (e.g. swipe back).
// In either case, update the DOM directly
if (!document.startViewTransition || e.hasUAVisualTransition) {
setContent(newContent);
return;
}
// Update the content using a View Transition
const t = document.startViewTransition(() => {
setContent(newContent);
});
}
});
});
บางเบราว์เซอร์ (แต่ไม่ใช่ทั้งหมด) จะมีการเปลี่ยนหน้าของตนเองเมื่อผู้ใช้ทำท่าทางสัมผัสการปัดเพื่อไปยังส่วนต่างๆ ในกรณีนี้ คุณไม่ควรทริกเกอร์การเปลี่ยนมุมมองของตัวเอง เนื่องจากจะทําให้ผู้ใช้ได้รับประสบการณ์ที่ไม่ดีหรือสับสน ผู้ใช้จะเห็นการเปลี่ยน 2 แบบ คือแบบแรกมาจากเบราว์เซอร์ และอีกแบบหนึ่งเป็นการทำงานแบบต่อเนื่องกัน
ดังนั้น เราขอแนะนำให้ป้องกันไม่ให้การเปลี่ยนมุมมองเริ่มต้นขึ้นเมื่อเบราว์เซอร์มีการเปลี่ยนภาพของตนเอง หากต้องการทำเช่นนั้น ให้ตรวจสอบค่าของพร็อพเพอร์ตี้ hasUAVisualTransition
ของอินสแตนซ์ NavigateEvent
ระบบจะตั้งค่าพร็อพเพอร์ตี้เป็น true
เมื่อเบราว์เซอร์มีการเปลี่ยนภาพ พร็อพเพอร์ตี้ hasUIVisualTransition
นี้ยังอยู่ในอินสแตนซ์ PopStateEvent
ด้วย
ในสนิปเพลตก่อนหน้า การตรวจสอบที่กําหนดว่าจะเรียกใช้การเปลี่ยนมุมมองหรือไม่จะพิจารณาพร็อพเพอร์ตี้นี้ หากไม่รองรับการเปลี่ยนมุมมองเอกสารเดียวกันหรือเมื่อเบราว์เซอร์ทำการเปลี่ยนของตัวเองอยู่แล้ว ระบบจะข้ามการเปลี่ยนมุมมอง
if (!document.startViewTransition || e.hasUAVisualTransition) {
setContent(newContent);
return;
}
ในวิดีโอต่อไปนี้ ผู้ใช้ปัดเพื่อไปยังหน้าก่อนหน้า ภาพด้านซ้ายไม่ได้ตรวจสอบธง hasUAVisualTransition
การบันทึกทางด้านขวามีการตรวจสอบ จึงข้ามการเปลี่ยนมุมมองด้วยตนเองเนื่องจากเบราว์เซอร์มีการเปลี่ยนภาพ
การสร้างภาพเคลื่อนไหวด้วย 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 และโค้ดแบบแอซิงโครนัส เมื่อใช้ Promise ต่างๆ ที่startViewTransition
แสดงผล ให้ตรวจสอบว่าโค้ดทำงานด้วยสถานะที่ถูกต้อง - Vue.js - คีย์ในที่นี้คือ
nextTick
ซึ่งจะตอบสนองเมื่ออัปเดต DOM แล้ว - Svelte - คล้ายกับ Vue มาก แต่วิธีการรอการเปลี่ยนแปลงครั้งต่อไปคือ
tick
- Lit - สิ่งสำคัญคือ Promise
this.updateComplete
ภายในคอมโพเนนต์ ซึ่งจะดำเนินการเมื่อ DOM ได้รับการอัปเดต - Angular - คีย์คือ
applicationRef.tick
ซึ่งจะล้างการเปลี่ยนแปลง DOM ที่รอดำเนินการ ตั้งแต่ Angular เวอร์ชัน 17 เป็นต้นไป คุณสามารถใช้withViewTransitions
ที่มาพร้อมกับ@angular/router
ได้
เอกสารอ้างอิง API
const viewTransition = document.startViewTransition(update)
เริ่ม
ViewTransition
ใหม่update
เป็นฟังก์ชันที่จะเรียกใช้เมื่อบันทึกสถานะปัจจุบันของเอกสารจากนั้นเมื่อ Promise ที่
updateCallback
แสดงผลจะเป็นไปตามที่คาดไว้ การเปลี่ยนเฟรมจะเริ่มขึ้นในเฟรมถัดไป หาก Promise ที่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 แทน