iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0
Modern Web

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

GSAP 最簡單功能 + map 就可以掉落源源不絕的東西~ 觸發掉落特效!

  • 分享至 

  • xImage
  •  

嗨咿,我是 illumi,今天一起來用 GSAP 做這個觸發掉落的特效吧!
看著有很多變化,其實只是 物件的 y 改變而已,只用到最簡單的Tween !
Yes

首先 import 插件 ,並放入你準備的圖片:

import { useRef } from "react";
import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { bagel1, bagel2, bagel3, bagel4 } from "@/assets/bagel";

const IMAGES = [bagel1, bagel2, bagel3, bagel4];

再來要給 gsap 一個控制的 ref

const Drop = () => {
  const containerRef = useRef<HTMLDivElement>(null);

	return (
    <div ref={containerRef} >
     
    </div>
  );
};

export default Drop;

因為只有四張圖,想要有掉下很多的感覺,所以用 map 來增生!

const cols = IMAGES.length; // 4
const rows = 4; // 4 × 4 = 16
const box = 100; // 圖片寬高 (px)

const Drop = ({ className }: { className?: string }) => {
  const containerRef = useRef<HTMLDivElement>(null);

  const items = Array.from({ length: rows * cols }, (_, i) => IMAGES[i % cols]);

  return (
    <div ref={containerRef} className={`relative mx-auto ${className}`}>
      {items.map((src, idx) => {
       
          <img
            key={idx}
            src={src}
            className="falling w-24 h-24"
            alt={`box-${idx}`}
          />
        );
      })}
    </div>
  );
};

export default Drop;

然後寫gsap 控制

useGSAP(
    () => {
      const nodes = gsap.utils.toArray<HTMLImageElement>(".falling");

      nodes.forEach((el, idx) => {
        gsap.fromTo(
          el,
          { y: -400, opacity: 0 },
          {
            y: 0,
            opacity: 1,
            ease: "bounce.out", // 模擬落地彈跳
            duration: 0.5,
            delay: idx * 0.05, // 依序掉落
          }
        );
      });
    },
    { scope: containerRef }
  ); // 使用 scope 限制動畫範圍

如果沒有寫落下位置,他會非常整整齊齊,為了營造隨機感:

const box = 100; // 圖片寬高 (px)

{items.map((src, idx) => {
        // left: 隨機在容器寬度內(不超出)
        const left = Math.random() * (800 - box);
        // 落點 bottom: 0 ~ bottom: 20 之間
        const minBottom = 0;
        const maxBottom = 0;
        const bottom = minBottom + Math.random() * (maxBottom - minBottom);
        const rotate = Math.random() * 60 - 30;
        return (
          <img
            key={idx}
            src={src}
            className="falling w-24 h-24"
            style={{
              width: box,
              height: box,
              position: "absolute",
              left: Math.max(0, Math.min(left, 800 - box)),
              bottom: Math.max(0, Math.min(bottom, 0)),
              userSelect: "none",
              transform: `rotate(${rotate}deg)`,
            }}
            alt={`box-${idx}`}
          />
        );
      })}

父層再用 onclickonMouseEnter 觸發就 ok 了!

Yes

附上程式碼:

import { useRef } from "react";
import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { bagel1, bagel2, bagel3, bagel4 } from "@/assets/bagel";

const IMAGES = [bagel1, bagel2, bagel3, bagel4];

const cols = IMAGES.length; // 4
const rows = 4; // 4 × 4 = 16
const box = 100; // 圖片寬高 (px)

const Drop = ({ className }: { className?: string }) => {
  const containerRef = useRef<HTMLDivElement>(null);

  const items = Array.from({ length: rows * cols }, (_, i) => IMAGES[i % cols]);

  useGSAP(
    () => {
      const nodes = gsap.utils.toArray<HTMLImageElement>(".falling");

      nodes.forEach((el, idx) => {
        gsap.fromTo(
          el,
          { y: -400, opacity: 0 },
          {
            y: 0,
            opacity: 1,
            ease: "bounce.out", // 模擬落地彈跳
            duration: 0.5,
            delay: idx * 0.05, // 依序掉落
          }
        );
      });
    },
    { scope: containerRef }
  ); // 使用 scope 限制動畫範圍

  return (
    <div ref={containerRef} className={`relative mx-auto ${className}`}>
      {items.map((src, idx) => {
        // left: 隨機在容器寬度內(不超出)
        const left = Math.random() * (800 - box);
        // 落點 bottom: 0 ~ bottom: 20 之間
        const minBottom = 0;
        const maxBottom = 0;
        const bottom = minBottom + Math.random() * (maxBottom - minBottom);
        const rotate = Math.random() * 60 - 30;
        return (
          <img
            key={idx}
            src={src}
            className="falling w-24 h-24"
            style={{
              width: box,
              height: box,
              position: "absolute",
              left: Math.max(0, Math.min(left, 800 - box)),
              bottom: Math.max(0, Math.min(bottom, 0)),
              userSelect: "none",
              transform: `rotate(${rotate}deg)`,
            }}
            alt={`box-${idx}`}
          />
        );
      })}
    </div>
  );
};

export default Drop;


上一篇
超簡單就能下載的GSAP全面特效庫,但寫 React 的先等等,你們下載方式不一樣喔
系列文
在Vibe Coding 時代一起來做沒有AI感的漂亮網站吧!13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言