ข้ามไปยังเนื้อหา

View Transitions

เว็บ multi-page แบบดั้งเดิมจะ reload ทั้ง browser ทุกครั้งที่ navigate — เชื่อถือได้ แต่หน้าจอจะแฟลชขาวและ scroll position รีเซ็ต ส่วน SPA เลี่ยงเรื่องนั้นได้แต่ต้องแลกด้วย JavaScript router ที่หนัก view transitions ของ Astro ให้ความรู้สึกลื่นแบบ SPA บนเว็บ MPA ปกติ ด้วย script แค่ไม่กี่กิโลไบต์

เรา opt in ด้วยการเพิ่ม component เดียวคือ <ClientRouter /> เข้าไปใน <head> ของหน้า — ปกติวางใน layout ที่ใช้ร่วมกันเพื่อให้มีผลทั้งเว็บ:

---
import { ClientRouter } from 'astro:transitions';
---
<html lang="en">
<head>
<title>My site</title>
<ClientRouter />
</head>
<body>
<slot />
</body>
</html>

พอมีตัวนี้แล้ว การ navigate ระหว่างหน้าในเว็บของเราจะถูก intercept และจัดการฝั่ง client: Astro fetch หน้าถัดไป, swap content เข้ามา และ animate การเปลี่ยน — ไม่มี full reload browser ที่ไม่รองรับ API เบื้องหลังจะ fallback ไปเป็น navigation ปกติ ทุกอย่างจึงไม่พัง

flowchart LR
  click["User clicks a link"] --> intercept["ClientRouter intercepts (no full reload)"]
  intercept --> fetch["Fetch the next page HTML"]
  fetch --> swap["Swap in the new content, animate"]
  swap --> event["Fire astro:page-load"]
ClientRouter swaps content instead of reloading

โดย default Astro จะ cross-fade หน้า เราคุม animation ต่อ element ได้ด้วย directive:

  • transition:name — ตั้งชื่อเดียวกันให้ element บนทั้งสองหน้า เพื่อให้ Astro morph จากอันหนึ่งไปอีกอัน (shared-element transition เช่น thumbnail ที่ค่อย ๆ ขยายเป็น hero image)
  • transition:animate — เลือก animation (fade, slide หรือแบบ custom) import helper จาก astro:transitions
  • transition:persist — เก็บ DOM และ state ของ component ไว้ข้าม navigation แทนที่จะ re-render เหมาะกับ video ที่กำลังเล่นหรือ audio player ที่ไม่ควรเริ่มใหม่
---
import { slide } from 'astro:transitions';
---
<!-- This heading slides between pages; the player keeps playing across nav. -->
<h1 transition:name="page-title" transition:animate={slide({ duration: 200 })}>Title</h1>
<video controls autoplay transition:name="player" transition:persist />

เพราะ navigation ไม่ trigger full page load อีกต่อไป script ที่รันตอน DOMContentLoaded จะไม่รันซ้ำตอน navigate แบบ client-side Astro จึง fire event ของตัวเองให้เรา re-initialize:

  • astro:page-load — fire หลังทุก navigation (ทั้ง load ครั้งแรก และ การ swap แบบ client-side) วาง initialization ไว้ตรงนี้แทน DOMContentLoaded
  • astro:after-swap — fire ทันทีหลัง DOM ใหม่ถูก swap เข้ามา ก่อน paint (เหมาะกับการ restore theme/state เพื่อเลี่ยงการแฟลช)
document.addEventListener('astro:page-load', () => {
// runs on first load AND after every client-side navigation
initAnalytics();
});

นี่คือ gotcha ที่พบบ่อยที่สุดของ view transitions: init script ที่ทำงานตอน load ครั้งแรกแต่ “หยุดทำงาน” หลัง navigate เพราะผูกกับ DOMContentLoaded ให้ย้ายไปที่ astro:page-load

เปิด view transitions ใน Astro ยังไง?
ชื่อปัจจุบันของ component นี้คืออะไร (Astro รุ่นเก่าเรียกชื่ออื่น)?
`transition:persist` ทำอะไร?
init script ที่ผูกกับ `DOMContentLoaded` หยุดทำงานหลัง navigate แบบ client-side แก้ยังไง?