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

เมื่อการเปลี่ยนมุมมองเกิดขึ้นระหว่างเอกสาร 2 ฉบับ เรียกว่าการเปลี่ยนมุมมองข้ามเอกสาร ซึ่งมักเกิดขึ้นในแอปพลิเคชันหลายหน้า (MPA) Chrome ตั้งแต่เวอร์ชัน 126 รองรับการเปลี่ยนมุมมองข้ามเอกสาร

Browser Support

  • Chrome: 126.
  • Edge: 126.
  • Firefox: not supported.
  • Safari: 18.2.

Source

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

  1. เบราว์เซอร์จะจับภาพองค์ประกอบที่มี view-transition-name ที่ไม่ซ้ำกันทั้งในหน้าเก่าและหน้าใหม่
  2. DOM จะอัปเดตขณะที่ระงับการแสดงผล
  3. และสุดท้าย ทรานซิชันจะทำงานด้วยภาพเคลื่อนไหว CSS

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

กล่าวคือ ไม่มี API ให้เรียกใช้เพื่อเริ่มการเปลี่ยนมุมมองระหว่างเอกสาร 2 ฉบับ อย่างไรก็ตาม คุณต้องปฏิบัติตามเงื่อนไข 2 ข้อต่อไปนี้

  • เอกสารทั้ง 2 รายการต้องอยู่ในแหล่งที่มาเดียวกัน
  • ทั้ง 2 หน้าต้องเลือกใช้เพื่ออนุญาตให้เปลี่ยนมุมมอง

เงื่อนไขทั้ง 2 อย่างนี้มีคำอธิบายอยู่ในส่วนต่อๆ ไปของเอกสารนี้


การเปลี่ยนมุมมองข้ามเอกสารจะจำกัดไว้ที่การไปยังส่วนต่างๆ ในต้นทางเดียวกัน

การเปลี่ยนมุมมองข้ามเอกสารจะจำกัดไว้ที่การไปยังส่วนต่างๆ ในต้นทางเดียวกันเท่านั้น ระบบจะถือว่าการไปยังส่วนต่างๆ เป็นการไปยังส่วนต่างๆ ในต้นทางเดียวกันหากต้นทางของทั้ง 2 หน้าที่เข้าร่วมเหมือนกัน

ต้นทางของหน้าเว็บคือชุดค่าผสมของรูปแบบ ชื่อโฮสต์ และพอร์ตที่ใช้ ตามที่ระบุไว้ใน web.dev

ตัวอย่าง URL ที่ไฮไลต์รูปแบบ ชื่อโฮสต์ และพอร์ต เมื่อรวมกันก็จะกลายเป็นต้นทาง
ตัวอย่าง URL ที่ไฮไลต์สคีมา ชื่อโฮสต์ และพอร์ต เมื่อรวมกันก็จะกลายเป็นต้นทาง

เช่น คุณสามารถเปลี่ยนมุมมองข้ามเอกสารได้เมื่อไปยัง developer.chrome.com จาก developer.chrome.com/blog เนื่องจากเอกสารดังกล่าวมีต้นทางเดียวกัน คุณไม่สามารถใช้การเปลี่ยนดังกล่าวเมื่อไปยัง www.chrome.com จาก developer.chrome.com เนื่องจากเป็นเว็บไซต์จากต้นทางอื่นและอยู่ในเว็บไซต์เดียวกัน


การเปลี่ยนมุมมองข้ามเอกสารเป็นแบบเลือกใช้

หากต้องการให้มีการเปลี่ยนมุมมองข้ามเอกสารระหว่างเอกสาร 2 ฉบับ หน้าเว็บที่เข้าร่วมทั้ง 2 หน้าต้องเลือกรับเพื่ออนุญาตการดำเนินการนี้ ซึ่งทำได้ด้วย @view-transition at-rule ใน CSS

ใน @view-transition at-rule ให้ตั้งค่าตัวระบุ navigation เป็น auto เพื่อเปิดใช้การเปลี่ยนมุมมองสำหรับการไปยังส่วนต่างๆ ในเอกสารต้นทางเดียวกันข้ามเอกสาร

@view-transition {   navigation: auto; } 

การตั้งค่าตัวบ่งชี้ navigation เป็น auto หมายความว่าคุณเลือกที่จะอนุญาตให้มีการเปลี่ยนมุมมองสำหรับ NavigationType ต่อไปนี้

  • traverse
  • push หรือ replace หากผู้ใช้ไม่ได้เป็นผู้เริ่มเปิดใช้งานผ่านกลไก UI ของเบราว์เซอร์

การนำทางที่ยกเว้นจาก auto เช่น การนำทางโดยใช้แถบที่อยู่ URL หรือคลิกบุ๊กมาร์ก รวมถึงการโหลดซ้ำที่ผู้ใช้หรือสคริปต์เป็นผู้เริ่มในรูปแบบใดก็ตาม

หากการไปยังส่วนต่างๆ ใช้เวลานานเกินไป (มากกว่า 4 วินาทีสำหรับ Chrome) ระบบจะข้ามการเปลี่ยนมุมมองด้วย TimeoutError DOMException

การสาธิตการเปลี่ยนมุมมองข้ามเอกสาร

ดูการสาธิตต่อไปนี้ที่ใช้ทรานซิชันของมุมมองเพื่อสร้างการสาธิต Stack Navigator ไม่มีการเรียกใช้ document.startViewTransition() ที่นี่ การเปลี่ยนมุมมองจะทริกเกอร์โดยการไปยังหน้าเว็บอื่น

ไฟล์บันทึกวิดีโอการสาธิต Stack Navigator ต้องใช้ Chrome 126 ขึ้นไป

ปรับแต่งการเปลี่ยนมุมมองข้ามเอกสาร

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

ฟีเจอร์เหล่านี้ไม่ได้เป็นส่วนหนึ่งของข้อกําหนดของ View Transition API แต่ออกแบบมาเพื่อใช้ร่วมกับ API ดังกล่าว

เหตุการณ์ pageswap และ pagereveal

Browser Support

  • Chrome: 124.
  • Edge: 124.
  • Firefox: not supported.
  • Safari: 18.2.

Source

เพื่อให้คุณปรับแต่งการเปลี่ยนผ่านมุมมองข้ามเอกสารได้ ข้อกำหนด HTML จึงมีเหตุการณ์ใหม่ 2 รายการที่คุณใช้ได้ ได้แก่ pageswap และ pagereveal

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

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

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

let lastClickX, lastClickY; document.addEventListener('click', (event) => {   if (event.target.tagName.toLowerCase() === 'a') return;   lastClickX = event.clientX;   lastClickY = event.clientY; });  // Write position to storage on old page window.addEventListener('pageswap', (event) => {   if (event.viewTransition && lastClick) {     sessionStorage.setItem('lastClickX', lastClickX);     sessionStorage.setItem('lastClickY', lastClickY);   } });  // Read position from storage on new page window.addEventListener('pagereveal', (event) => {   if (event.viewTransition) {     lastClickX = sessionStorage.getItem('lastClickX');     lastClickY = sessionStorage.getItem('lastClickY');   } }); 

คุณเลือกข้ามการเปลี่ยนผ่านในทั้ง 2 กิจกรรมได้หากต้องการ

window.addEventListener("pagereveal", async (e) => {   if (e.viewTransition) {     if (goodReasonToSkipTheViewTransition()) {       e.viewTransition.skipTransition();     }   } } 

ออบเจ็กต์ ViewTransition ใน pageswap และออบเจ็กต์ pagereveal เป็นคนละออบเจ็กต์กัน นอกจากนี้ ยังจัดการคำมั่นสัญญาต่างๆ แตกต่างกันด้วย ดังนี้

  • pageswap: เมื่อซ่อนเอกสารแล้ว ระบบจะข้ามออบเจ็กต์ ViewTransition เดิม เมื่อเกิดเหตุการณ์นี้ขึ้น viewTransition.ready จะปฏิเสธและ viewTransition.finished จะแก้ไข
  • pagereveal: สัญญา updateCallBack ได้รับการแก้ไขแล้วในตอนนี้ คุณใช้การรับประกัน viewTransition.ready และ viewTransition.finished ได้

Browser Support

  • Chrome: 123.
  • Edge: 123.
  • Firefox: not supported.
  • Safari: not supported.

Source

ในทั้งเหตุการณ์ pageswap และ pagereveal คุณจะดําเนินการตาม URL ของหน้าเก่าและหน้าใหม่ได้ด้วย

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

  • เมื่อไปยังหน้ารายละเอียดจากหน้าภาพรวม เนื้อหาใหม่ต้องเลื่อนจากขวาไปซ้าย
  • เมื่อไปยังหน้าภาพรวมจากหน้ารายละเอียด เนื้อหาเก่าต้องเลื่อนจากซ้ายไปขวา

ซึ่งคุณจะต้องมีข้อมูลเกี่ยวกับการนําทางที่กำลังจะเกิดขึ้น (ในกรณี pageswap) หรือเพิ่งเกิดขึ้น (ในกรณี pagereveal)

ด้วยเหตุนี้ เบราว์เซอร์จึงสามารถแสดงออบเจ็กต์ NavigationActivation ซึ่งเก็บข้อมูลเกี่ยวกับการนําทางจากต้นทางเดียวกันได้แล้ว ออบเจ็กต์นี้จะแสดงประเภทการนําทางที่ใช้ รายการประวัติปลายทางปัจจุบัน และปลายทางสุดท้ายตามที่พบใน navigation.entries() จาก Navigation API

ในหน้าที่เปิดใช้งาน คุณจะเข้าถึงออบเจ็กต์นี้ผ่าน navigation.activation ได้ ในกิจกรรม pageswap คุณจะเข้าถึงข้อมูลนี้ได้ผ่าน e.activation

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

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

ไฟล์บันทึกเสียงของการสาธิตโปรไฟล์ ต้องใช้ Chrome 126 ขึ้นไป

รหัสมีดังนี้

// OLD PAGE LOGIC window.addEventListener('pageswap', async (e) => {   if (e.viewTransition) {     const targetUrl = new URL(e.activation.entry.url);      // Navigating to a profile page     if (isProfilePage(targetUrl)) {       const profile = extractProfileNameFromUrl(targetUrl);        // Set view-transition-name values on the clicked row       document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';       document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';        // Remove view-transition-names after snapshots have been taken       // (this to deal with BFCache)       await e.viewTransition.finished;       document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';       document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';     }   } });  // NEW PAGE LOGIC window.addEventListener('pagereveal', async (e) => {   if (e.viewTransition) {     const fromURL = new URL(navigation.activation.from.url);     const currentURL = new URL(navigation.activation.entry.url);      // Navigating from a profile page back to the homepage     if (isProfilePage(fromURL) && isHomePage(currentURL)) {       const profile = extractProfileNameFromUrl(currentURL);        // Set view-transition-name values on the elements in the list       document.querySelector(`#${profile} span`).style.viewTransitionName = 'name';       document.querySelector(`#${profile} img`).style.viewTransitionName = 'avatar';        // Remove names after snapshots have been taken       // so that we're ready for the next navigation       await e.viewTransition.ready;       document.querySelector(`#${profile} span`).style.viewTransitionName = 'none';       document.querySelector(`#${profile} img`).style.viewTransitionName = 'none';     }   } }); 

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

หากต้องการช่วยแก้ปัญหานี้ ให้ใช้ฟังก์ชันยูทิลิตีนี้ซึ่งตั้งค่า view-transition-name ไว้ชั่วคราว

const setTemporaryViewTransitionNames = async (entries, vtPromise) => {   for (const [$el, name] of entries) {     $el.style.viewTransitionName = name;   }    await vtPromise;    for (const [$el, name] of entries) {     $el.style.viewTransitionName = '';   } } 

ตอนนี้โค้ดก่อนหน้าจะเขียนให้เข้าใจง่ายขึ้นได้ดังนี้

// OLD PAGE LOGIC window.addEventListener('pageswap', async (e) => {   if (e.viewTransition) {     const targetUrl = new URL(e.activation.entry.url);      // Navigating to a profile page     if (isProfilePage(targetUrl)) {       const profile = extractProfileNameFromUrl(targetUrl);        // Set view-transition-name values on the clicked row       // Clean up after the page got replaced       setTemporaryViewTransitionNames([         [document.querySelector(`#${profile} span`), 'name'],         [document.querySelector(`#${profile} img`), 'avatar'],       ], e.viewTransition.finished);     }   } });  // NEW PAGE LOGIC window.addEventListener('pagereveal', async (e) => {   if (e.viewTransition) {     const fromURL = new URL(navigation.activation.from.url);     const currentURL = new URL(navigation.activation.entry.url);      // Navigating from a profile page back to the homepage     if (isProfilePage(fromURL) && isHomePage(currentURL)) {       const profile = extractProfileNameFromUrl(currentURL);        // Set view-transition-name values on the elements in the list       // Clean up after the snapshots have been taken       setTemporaryViewTransitionNames([         [document.querySelector(`#${profile} span`), 'name'],         [document.querySelector(`#${profile} img`), 'avatar'],       ], e.viewTransition.ready);     }   } }); 

รอให้เนื้อหาโหลดโดยมีการบล็อกการแสดงผล

Browser Support

  • Chrome: 124.
  • Edge: 124.
  • Firefox: not supported.
  • Safari: not supported.

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

ใน <head> ให้กําหนดรหัสองค์ประกอบอย่างน้อย 1 รายการที่ต้องแสดงก่อนหน้าที่จะได้รับการแสดงผลครั้งแรกโดยใช้เมตาแท็กต่อไปนี้

<link rel="expect" blocking="render" href="#section1"> 

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

ก่อนใช้การบล็อกการแสดงผลอย่างเต็มรูปแบบ โปรดทราบว่าการแสดงผลที่เพิ่มขึ้นเป็นองค์ประกอบพื้นฐานของเว็บ ดังนั้นโปรดระมัดระวังเมื่อเลือกบล็อกการแสดงผล ผลกระทบของการบล็อกการแสดงผลต้องได้รับการประเมินเป็นรายกรณี โดยค่าเริ่มต้น ให้หลีกเลี่ยงการใช้ blocking=render เว้นแต่คุณจะวัดและประเมินผลกระทบที่มีต่อผู้ใช้ได้ โดยวัดผลกระทบต่อ Core Web Vitals


ดูประเภทการเปลี่ยนในทรานซิชันของมุมมองข้ามเอกสาร

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

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

หากต้องการตั้งค่าประเภทเหล่านี้ล่วงหน้า ให้เพิ่มประเภทใน @view-transition at-rule

@view-transition {   navigation: auto;   types: slide, forwards; } 

หากต้องการตั้งค่าประเภทขณะดำเนินการ ให้ใช้เหตุการณ์ pageswap และ pagereveal เพื่อจัดการค่าของ e.viewTransition.types

window.addEventListener("pagereveal", async (e) => {   if (e.viewTransition) {     const transitionType = determineTransitionType(navigation.activation.from, navigation.activation.entry);     e.viewTransition.types.add(transitionType);   } }); 

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

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

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

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

สาธิต

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

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

ระบบจะกําหนดประเภทการเปลี่ยนไปใช้ในเหตุการณ์ pagereveal และ pageswap โดยดูที่ URL ต้นทางและปลายทาง

const determineTransitionType = (fromNavigationEntry, toNavigationEntry) => {   const currentURL = new URL(fromNavigationEntry.url);   const destinationURL = new URL(toNavigationEntry.url);    const currentPathname = currentURL.pathname;   const destinationPathname = destinationURL.pathname;    if (currentPathname === destinationPathname) {     return "reload";   } else {     const currentPageIndex = extractPageIndexFromPath(currentPathname);     const destinationPageIndex = extractPageIndexFromPath(destinationPathname);      if (currentPageIndex > destinationPageIndex) {       return 'backwards';     }     if (currentPageIndex < destinationPageIndex) {       return 'forwards';     }      return 'unknown';   } }; 

ความคิดเห็น

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