Smooth page transitions animation
การสร้าง transitions ที่ลื่นไหลด้วย Astro
โดย Astrobeginner Team 1 นาทีในการอ่าน advanced

คู่มือการใช้งาน View Transitions ใน Astro

เรียนรู้วิธีการสร้าง page transitions ที่ลื่นไหลและสวยงามด้วย Astro View Transitions API

#astro #view-transitions #animation #ux

คู่มือการใช้งาน View Transitions ใน Astro

View Transitions เป็นฟีเจอร์ใหม่ที่ทำให้เราสามารถสร้าง page transitions ที่ลื่นไหลและสวยงามได้โดยไม่ต้องใช้ JavaScript framework ที่ซับซ้อน ในบทความนี้เราจะมาเรียนรู้วิธีการใช้งาน View Transitions ใน Astro กัน

View Transitions คืออะไร?

View Transitions เป็น Web API ใหม่ที่ช่วยให้เราสามารถสร้าง animations ระหว่างการเปลี่ยนหน้าได้อย่างง่ายดาย โดยไม่ต้องโหลดหน้าใหม่ทั้งหมด

ข้อดีของ View Transitions:

  • ประสบการณ์ผู้ใช้ที่ดีขึ้น: การเปลี่ยนหน้าที่ลื่นไหล
  • ประสิทธิภาพดี: ใช้ browser native API
  • ง่ายต่อการใช้งาน: ไม่ต้องเขียน JavaScript ซับซ้อน
  • SEO-friendly: ยังคงเป็น static site

การเปิดใช้งาน View Transitions

ขั้นตอนที่ 1: เพิ่ม ViewTransitions Component

เพิ่ม ViewTransitions component ใน layout หลัก:

---
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions';
---

<html>
  <head>
    <title>{title}</title>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>

ขั้นตอนที่ 2: ใช้ transition directives

---
// src/components/Header.astro
---

<header transition:persist>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
    <a href="/articles">Articles</a>
  </nav>
</header>

Transition Directives

transition:persist

เก็บ element ไว้ข้ามหน้า:

<header transition:persist>
  <!-- Header จะไม่หายไประหว่างการเปลี่ยนหน้า -->
</header>

<audio transition:persist controls>
  <!-- เพลงจะเล่นต่อเนื่องระหว่างการเปลี่ยนหน้า -->
</audio>

transition:name

กำหนดชื่อให้กับ transition:

<!-- หน้า 1 -->
<main transition:name="main-content">
  <h1>หน้าแรก</h1>
</main>

<!-- หน้า 2 -->
<main transition:name="main-content">
  <h1>เกี่ยวกับเรา</h1>
</main>

transition:animate

กำหนด animation แบบกำหนดเอง:

<div transition:animate="slide">
  Content ที่จะ slide
</div>

<div transition:animate="fade">
  Content ที่จะ fade
</div>

การปรับแต่ง CSS Transitions

การสร้าง Custom Animations

/* src/styles/transitions.css */

/* Slide animation */
@keyframes slide-left {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0);
  }
}

@keyframes slide-right {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}

/* ใช้กับ View Transitions */
::view-transition-old(slide) {
  animation: slide-left 0.3s ease-out;
}

::view-transition-new(slide) {
  animation: slide-right 0.3s ease-out;
}

การปรับแต่ง Default Transitions

/* ปรับ duration และ easing */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.5s;
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Fade animation */
::view-transition-old(root) {
  animation-name: fade-out;
}

::view-transition-new(root) {
  animation-name: fade-in;
}

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

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

การใช้งานกับ JavaScript

การ Hook เข้ากับ Transition Events

// src/scripts/transitions.js

document.addEventListener('astro:page-load', () => {
  console.log('New page loaded');
  // Re-initialize components หลังจากเปลี่ยนหน้า
  initializeComponents();
});

document.addEventListener('astro:before-preparation', () => {
  console.log('About to prepare new page');
  // Cleanup ก่อนเปลี่ยนหน้า
  cleanupComponents();
});

function initializeComponents() {
  // เริ่มต้น components ใหม่
  const buttons = document.querySelectorAll('.interactive-button');
  buttons.forEach(button => {
    button.addEventListener('click', handleClick);
  });
}

function cleanupComponents() {
  // ทำความสะอาด event listeners
  const buttons = document.querySelectorAll('.interactive-button');
  buttons.forEach(button => {
    button.removeEventListener('click', handleClick);
  });
}

การใช้ Navigation API

// Programmatic navigation with transitions
function navigateToPage(url) {
  // ใช้ browser native navigation
  window.location.href = url;
  
  // หรือใช้ History API
  history.pushState(null, '', url);
}

Advanced Techniques

การสร้าง Shared Element Transitions

---
// src/pages/articles/index.astro
---

<div class="article-grid">
  {articles.map(article => (
    <article transition:name={`article-${article.id}`}>
      <img src={article.image} alt={article.title} />
      <h2>{article.title}</h2>
      <a href={`/articles/${article.slug}`}>Read more</a>
    </article>
  ))}
</div>
---
// src/pages/articles/[slug].astro
---

<article transition:name={`article-${article.id}`}>
  <img src={article.image} alt={article.title} />
  <h1>{article.title}</h1>
  <div class="content">
    <slot />
  </div>
</article>

การใช้กับ Forms

<form transition:persist>
  <input type="text" name="search" placeholder="Search...">
  <button type="submit">Search</button>
</form>

<script>
  // รักษาค่าใน form ระหว่างการเปลี่ยนหน้า
  document.addEventListener('astro:before-preparation', () => {
    const formData = new FormData(document.querySelector('form'));
    sessionStorage.setItem('formData', JSON.stringify(Object.fromEntries(formData)));
  });

  document.addEventListener('astro:page-load', () => {
    const savedData = sessionStorage.getItem('formData');
    if (savedData) {
      const data = JSON.parse(savedData);
      Object.entries(data).forEach(([key, value]) => {
        const input = document.querySelector(`[name="${key}"]`);
        if (input) input.value = value;
      });
    }
  });
</script>

การเพิ่ม Loading States

---
// src/components/LoadingIndicator.astro
---

<div id="loading-indicator" class="fixed top-4 right-4 bg-blue-500 text-white px-4 py-2 rounded-lg shadow-lg transform translate-x-full transition-transform opacity-0">
  <div class="flex items-center space-x-2">
    <div class="animate-spin rounded-full h-4 w-4 border-b-2 border-white"></div>
    <span>Loading...</span>
  </div>
</div>

<script>
  document.addEventListener('astro:before-preparation', () => {
    const indicator = document.getElementById('loading-indicator');
    indicator.classList.remove('translate-x-full', 'opacity-0');
    indicator.classList.add('translate-x-0', 'opacity-100');
  });

  document.addEventListener('astro:page-load', () => {
    const indicator = document.getElementById('loading-indicator');
    setTimeout(() => {
      indicator.classList.add('translate-x-full', 'opacity-0');
      indicator.classList.remove('translate-x-0', 'opacity-100');
    }, 300);
  });
</script>

การจัดการ Accessibility

/* ให้เคารพ prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation-duration: 0.01s !important;
    animation-iteration-count: 1 !important;
  }
}
// เพิ่ม ARIA announcements
document.addEventListener('astro:page-load', () => {
  const title = document.title;
  const announcement = document.createElement('div');
  announcement.setAttribute('aria-live', 'polite');
  announcement.setAttribute('aria-atomic', 'true');
  announcement.className = 'sr-only';
  announcement.textContent = `Navigated to ${title}`;
  
  document.body.appendChild(announcement);
  
  setTimeout(() => {
    document.body.removeChild(announcement);
  }, 1000);
});

Best Practices

1. ใช้ transition:persist อย่างรอบคอบ

<!-- ดี: ใช้กับ elements ที่ต้องการให้คงอยู่ -->
<header transition:persist>
<nav transition:persist>
<audio transition:persist>

<!-- ไม่ดี: ใช้กับทุก element -->
<div transition:persist>
  <p transition:persist>Some text</p>
</div>

2. ตั้งชื่อ transitions ให้มีความหมาย

<!-- ดี -->
<main transition:name="main-content">
<aside transition:name="sidebar">
<article transition:name={`article-${id}`}>

<!-- ไม่ดี -->
<div transition:name="thing1">
<div transition:name="stuff">

3. ทดสอบกับ Network Throttling

// เพิ่ม timeout สำหรับ slow connections
const NAVIGATION_TIMEOUT = 5000;

let navigationTimer;

document.addEventListener('astro:before-preparation', () => {
  navigationTimer = setTimeout(() => {
    console.warn('Navigation taking too long');
    // แสดง error message หรือ fallback
  }, NAVIGATION_TIMEOUT);
});

document.addEventListener('astro:page-load', () => {
  clearTimeout(navigationTimer);
});

สรุป

View Transitions ใน Astro ช่วยให้เราสามารถสร้างประสบการณ์ผู้ใช้ที่ดีขึ้นได้อย่างง่ายดาย ด้วยการใช้:

  1. ViewTransitions component สำหรับเปิดใช้งาน
  2. transition directives สำหรับควบคุม elements
  3. CSS animations สำหรับปรับแต่งการแสดงผล
  4. JavaScript hooks สำหรับจัดการ lifecycle
  5. Accessibility considerations สำหรับผู้ใช้ทุกคน

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

แชร์บทความนี้

🐦 Twitter 📘 Facebook

เกี่ยวกับผู้เขียน

A

Astrobeginner Team

ทีมงาน Astrobeginner ที่หลงใหลในการพัฒนาเว็บไซต์สมัยใหม่ด้วย Astro และเทคโนโลยีต่างๆ

บทความที่เกี่ยวข้อง