iT邦幫忙

2025 iThome 鐵人賽

DAY 19
0
Modern Web

在Vibe Coding 時代一起來做沒有AI感的漂亮網站吧!系列 第 19

GSAP ScrollTrigger 讓物件隨著滑動說出故事

  • 分享至 

  • xImage
  •  

你有沒有很常看過具有連續說故事感的網頁,例如從咖啡豆變成咖啡、從雞蛋和麵粉變成鬆餅(板塊設計)。

我曾經也超級好奇,那些物件是怎麼如此絲滑倒車入庫,放入最適當的位置,貫穿全場的?

適用 GSAP Timeline + ScrollTrigger,讓畫面中的物件隨著滾動一步步變化位置、縮放,甚至翻轉。讓網頁不只是單純上下滑,而是帶有動畫過渡的「故事感」。

Yes
(這次總算換了一個專案玩了!可喜可賀)


STEP 1. 建立基本結構

首先,我們需要一個 主要容器 section,裡面放多個「場景」。

另外,在畫面中央放主要要移動的物件,並透過 absolute 定位在畫面。

<section ref={mainRef} className="relative scroll-container z-10 text-6xl font-bold">
  <div id="section1" className="scroll-section h-screen bg-amber-50 flex items-center justify-center">
    <h2>Introduction</h2>
  </div>
  <div id="section2" className="scroll-section h-screen bg-blue-200 flex items-center justify-center">
    <h2>My Skills</h2>
  </div>
  <div id="section3" className="scroll-section h-screen bg-violet-200 flex items-center justify-center">
    <h2>Projects</h2>
  </div>
  <div id="section4" className="scroll-section h-screen bg-red-200 flex items-center justify-center">
    <h2>Contact</h2>
  </div>
</section>

主要要移動的物件獨立放外層:

<img
  ref={boxRef}
  src=""
  alt=""
  className="w-80 z-20 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
  style={{ transformStyle: 'preserve-3d' }} // 讓 3D 翻轉可見
/>


STEP 2. 註冊 ScrollTrigger + 建立時間軸

使用 useGSAP Hook 來管理動畫。GSAP Timeline 好處是:

  • 可以把一系列動畫串起來。
  • 配合 ScrollTrigger,會自動依滾動長度分配動畫進度。
useGSAP(() => {
  const box = boxRef.current;

  const tl = gsap.timeline({
    scrollTrigger: {
      trigger: mainRef.current, // 綁定大容器
      pin: box,                 // 固定角色圖片
      scrub: 1,                 // 平滑跟隨捲動
      start: "top top",         // 從容器頂部開始
      end: "bottom bottom",     // 到容器底部結束
    }
  });

屬性解釋

  • trigger:觸發點,這裡是 mainRef,也就是整個大區塊。
  • pin:將 box(角色圖片)釘住,不會跟著頁面滑掉。
  • scrub1 表示滾動與動畫同步,並且加上一點平滑過渡。
  • start/end:控制動畫範圍,從「進入視窗頂部」到「整個區塊滑完」。

STEP 3. 設定不同 Section 對應的動畫

希望每個 Section 對應角色的一個狀態。

GSAP Timeline 的特點是:不同 to 會被均勻分配到滾動範圍

這裡 x、y 要移動多少就需要慢慢試或靠經驗,建議都是用vh vw,RWD才好控制。

  // Section 1 (初始狀態)
  tl.to(box, { duration: 1, ease: "none" });

  // Section 2 (角色往右下 & 縮小)
  tl.to(box, {
    x: "25vw",
    y: "10vh",
    scale: 0.8,
    duration: 1,
    ease: "none"
  });

  // Section 3 (角色往左上 & 放大 & 翻轉)
  tl.to(box, {
    x: "-20vw",
    y: "-10vh",
    scale: 1.2,
    rotationY: 180, // 翻轉 180 度
    duration: 1,
    ease: "none"
  });

  // Section 4 (回到中央 & 縮小)
  tl.to(box, {
    x: "0vw",
    y: "0vh",
    scale: 0.5,
    rotationY: 360, // 轉回正面
    duration: 1,
    ease: "none"
  });

  // 結尾加點留白
  tl.to(box, { duration: 0.5, ease: "none" });
}, { scope: mainRef });


好的完成啦!(ง •̀_•́)ง

Yes
接下來就可以在原本網頁其他內容了!

完整程式碼

以下程式碼可以直接貼上測試(需要安裝 gsap@gsap/react):

import Header from '../component/header.jsx';
import React, { useRef } from 'react';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { useGSAP } from '@gsap/react';

gsap.registerPlugin(ScrollTrigger);

const Scroll = () => {
  const mainRef = useRef(null);
  const boxRef = useRef(null);

  useGSAP(() => {
    const box = boxRef.current;

    const tl = gsap.timeline({
      scrollTrigger: {
        trigger: mainRef.current,
        pin: box,
        scrub: 1,
        start: 'top top',
        end: 'bottom bottom',
      }
    });

    tl.to(box, { duration: 1, ease: 'none' });
    tl.to(box, { x: '25vw', y: '10vh', scale: 0.8, duration: 1, ease: 'none' });
    tl.to(box, { x: '-20vw', y: '-10vh', scale: 1.2, rotationY: 180, duration: 1, ease: 'none' });
    tl.to(box, { scale: 0.5, rotationY: 360, duration: 1, ease: 'none' });
    tl.to(box, { duration: 0.5, ease: 'none' });
  }, { scope: mainRef });

  return (
    <>
      <Header className="mb-16" />
      <img
        ref={boxRef}
        src="你的圖"
        alt=""
        className="w-80 z-20 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
        style={{ transformStyle: 'preserve-3d' }}
      />
      <section ref={mainRef} className="relative scroll-container z-10 text-6xl font-bold">
        <div id="section1" className="scroll-section h-screen flex items-center justify-center bg-amber-50">
          <h2>Introduction</h2>
        </div>
        <div id="section2" className="scroll-section h-screen flex items-center justify-center bg-blue-200">
          <h2>My Skills</h2>
        </div>
        <div id="section3" className="scroll-section h-screen flex items-center justify-center bg-violet-200">
          <h2>Projects</h2>
        </div>
        <div id="section4" className="scroll-section h-screen flex items-center justify-center bg-red-200">
          <h2>Contact</h2>
        </div>
      </section>
    </>
  );
};

export default Scroll;



上一篇
當物理學介入GSAP掉落動畫:Matter.js
下一篇
UI做出了爆難做的 light/dark mode tab……直接送你react+tailwind寫的元件和售後服務,從此你就是掌管網頁黑夜與白天的神
系列文
在Vibe Coding 時代一起來做沒有AI感的漂亮網站吧!21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言