简介
Anime.js 是一个轻量级的 JavaScript 动画库,它提供了简单而强大的 API 来创建各种复杂的动画效果。以下是 Anime.js 的主要使用方法和特性:
安装
npm install animejs
示例
基本用法
import { animate, createScope, createSpring, createDraggable } from "animejs";
import { useEffect, useRef, useState } from "react";
import reactLogo from "@/assets/react.svg";
export default function App() {
const scope = useRef(null);
const [rotations, setRotations] = useState(0);
useEffect(() => {
scope.current = createScope({ root }).add((scope) => {
animate(".logo", {
scale: [
{ to: 1.25, ease: "inOut(3)", duration: 200 },
{ to: 1, ease: createSpring({ stiffness: 300 }) },
],
loop: true,
loopDelay: 250,
});
createDraggable(".logo", {
container: [0, 0, 0, 0],
releaseEase: createSpring({ stiffness: 300 }),
});
scope.add("rotateLogo", (i) => {
animate(".logo", {
rotate: i * 360,
ease: "out(4)",
duration: 2000,
});
});
});
return () => {
scope.current.revert();
};
}, []);
const handleClick = () => {
const i = rotations + 1;
setRotations(i);
scope.current.methods.rotateLogo(i);
};
return (
<div className="mt-10">
<img src={reactLogo} className="logo" alt="React logo" />
<button
className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors mt-4"
onClick={handleClick}
>
旋转
</button>
</div>
);
}
定时器(timer)
import { createTimer } from "animejs";
import { useEffect, useRef } from "react";
export default function Timer() {
const timeRef = useRef(null);
const countRef = useRef(null);
useEffect(() => {
createTimer({
duration: 1000,
loop: true,
frameRate: 30,
onUpdate: (self) => {
if (timeRef.current) {
timeRef.current.innerHTML = self.currentTime;
}
},
onLoop: (self) => {
if (countRef.current) {
countRef.current.innerHTML = self._currentIteration;
}
},
});
}, []);
return (
<div className="m-10 container mx-auto max-w-2xl">
<div className="flex flex-row justify-center items-center gap-8">
<div className="relative w-64 h-24 rounded-lg overflow-hidden shadow-lg bg-[#6d402a] flex flex-col items-center justify-center">
<span className="text-lg text-white mb-2">current time</span>
<span
ref={timeRef}
className="text-6xl font-mono font-bold tracking-widest text-[#ffa94d] lcd"
>
0
</span>
</div>
<div className="relative w-64 h-24 rounded-lg overflow-hidden shadow-lg bg-[#6d402a] flex flex-col items-center justify-center">
<span className="text-lg text-white mb-2">callback fired</span>
<span
ref={countRef}
className="text-6xl font-mono font-bold tracking-widest text-[#ffa94d] lcd"
>
0
</span>
</div>
</div>
</div>
);
}
时间线(timeline)
import { useEffect } from "react";
import { createTimeline } from "animejs";
export default function TimelineDemo() {
useEffect(() => {
const tl = createTimeline({ defaults: { duration: 750 } });
tl.add(".square", { x: "15rem" })
.add(".circle", { x: "15rem" })
.add(".triangle", { x: "15rem", rotate: "1turn" });
}, []);
return (
<div className="flex gap-4 mt-10">
<div className="square w-16 h-16 bg-red-400 rounded"></div>
<div className="circle w-16 h-16 bg-green-400 rounded-full"></div>
<div
className="triangle"
style={{
width: 0,
height: 0,
borderLeft: "32px solid transparent",
borderRight: "32px solid transparent",
borderBottom: "64px solid #60a5fa",
}}
></div>
</div>
);
}
动画(animate)
import { animate } from "animejs";
import { useRef } from "react";
export default function AnimateDemo() {
const ref = useRef(null);
const handleAnimate = () => {
animate(ref.current, {
opacity: [0, 1],
scale: [0.5, 1.2, 1],
rotate: [0, 360],
duration: 1200,
ease: "inOut(3)",
});
};
return (
<div className="mt-10 flex flex-col items-center">
<div ref={ref} className="w-24 h-24 bg-purple-400 rounded mb-4"></div>
<button
onClick={handleAnimate}
className="px-4 py-2 bg-purple-600 text-white rounded"
>
动画
</button>
</div>
);
}
拖动(drag)
import { useEffect, useRef } from "react";
import { createDraggable } from "animejs";
export default function DraggableDemo() {
const containerRef = useRef(null);
useEffect(() => {
if (containerRef.current) {
createDraggable(".square", {
container: containerRef.current, // 限定在容器内拖动
releaseEase: "out(3)",
});
}
}, []);
return (
<div
ref={containerRef}
className="mt-10 w-[400px] h-[200px] bg-gray-100 relative flex items-center justify-center"
>
<div className="square w-16 h-16 bg-yellow-400 rounded absolute top-0 left-0 cursor-move"></div>
</div>
);
}
滚动(scroll)
import { animate } from "animejs";
import { useEffect, useRef } from "react";
export default function ScrollDemo() {
// 用ref存储动画状态,避免重复渲染
const lastProgress = useRef(0);
const ticking = useRef(false);
useEffect(() => {
const onScroll = () => {
if (!ticking.current) {
window.requestAnimationFrame(() => {
const scrollY = window.scrollY;
const docHeight = document.body.scrollHeight - window.innerHeight;
const progress = Math.min(scrollY / docHeight, 1);
// 只在进度有明显变化时才触发动画
if (Math.abs(progress - lastProgress.current) > 0.001) {
lastProgress.current = progress;
// 1. 粉色盒子:分段动画
if (progress < 0.33) {
// 第一段:下移+放大
animate(".scroll-box", {
translateY: progress * 600,
scale: 1 + progress * 1.5,
opacity: 1,
rotate: 0,
background: "#f472b6",
duration: 400,
ease: "out(3)",
});
} else if (progress < 0.66) {
// 第二段:旋转+变色
animate(".scroll-box", {
translateY: 200 + (progress - 0.33) * 600,
scale: 1.5,
opacity: 1 - (progress - 0.33) * 1.5,
rotate: (progress - 0.33) * 720,
background: "#fbbf24",
duration: 400,
ease: "inOut(2)",
});
} else {
// 第三段:缩小+淡出
animate(".scroll-box", {
translateY: 400 + (progress - 0.66) * 600,
scale: 1.5 - (progress - 0.66) * 1.5,
opacity: 0.5 - (progress - 0.66) * 1.5,
rotate: 360,
background: "#34d399",
duration: 400,
ease: "in(2)",
});
}
// 2. 蓝色盒子:左右来回移动+弹性
animate(".scroll-box2", {
translateX: Math.sin(progress * Math.PI * 2) * 300,
scale: 1 + Math.abs(Math.cos(progress * Math.PI)),
rotate: progress * 360,
background: "#60a5fa",
duration: 500,
ease: "outElastic(1, .5)",
});
// 3. 进度条
animate(".scroll-progress", {
scaleX: progress,
duration: 200,
ease: "linear",
});
// 4. 文字渐显
animate(".scroll-text", {
opacity: progress,
translateY: 100 - progress * 100,
duration: 400,
ease: "out(2)",
});
}
ticking.current = false;
});
ticking.current = true;
}
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
return (
<div className="h-[2500px] relative">
{/* 滚动进度条 */}
<div className="fixed top-0 left-0 w-full h-2 bg-gray-200 z-50">
<div className="scroll-progress origin-left h-full bg-pink-400 scale-x-0"></div>
</div>
{/* 动画盒子1 */}
<div className="scroll-box w-32 h-32 bg-pink-400 fixed top-20 left-20 rounded-lg shadow-lg"></div>
{/* 动画盒子2 */}
<div className="scroll-box2 w-32 h-32 bg-blue-400 fixed top-60 left-60 rounded-lg shadow-lg"></div>
{/* 渐显文字 */}
<div className="scroll-text fixed top-[300px] left-1/2 -translate-x-1/2 w-[600px] text-3xl text-gray-700 opacity-0">
<p>分段动画、弹性、渐变、旋转、缩放、透明度,全部联动!</p>
<p className="mt-10 text-xl">继续滚动,体验更丰富的滚动动画效果。</p>
</div>
{/* 内容填充 */}
<div className="absolute top-[700px] left-1/2 -translate-x-1/2 w-[600px] text-xl text-gray-700">
<p>你可以继续添加更多动画元素,或根据滚动区间分段控制动画效果。</p>
</div>
</div>
);
}
作用域(Scope)
import { useEffect } from "react";
import { animate, utils, createScope } from "animejs";
export default function ScopeDemo() {
useEffect(() => {
// 创建 scope,带媒体查询
const scope = createScope({
mediaQueries: {
isSmall: "(max-width: 500px)",
reduceMotion: "(prefers-reduced-motion)",
},
}).add((self) => {
const { isSmall, reduceMotion } = self.matches;
// 响应式设置缩放
if (isSmall) {
utils.set(".square", { scale: 0.5 });
} else {
utils.set(".square", { scale: 1 });
}
// 响应式动画
animate(".square", {
x: isSmall ? 0 : ["-35vw", "35vw"],
y: isSmall ? ["-40vh", "40vh"] : 0,
loop: true,
alternate: true,
duration: reduceMotion ? 0 : isSmall ? 750 : 1250,
easing: "inOutSine",
});
});
// 卸载时清理
return () => scope.revert();
}, []);
return (
<div className="mt-10 flex justify-center items-center h-[60vh] bg-slate-200">
<div className="square w-24 h-24 bg-cyan-400 rounded"></div>
</div>
);
}
错峰(Alternate)
import { animate, stagger } from "animejs";
import { useEffect } from "react";
export default function StaggerDemo() {
useEffect(() => {
animate(".stagger-item", {
translateY: [300, 0],
scale: [0.3, 1],
rotate: [-90, 0],
opacity: [0, 1],
delay: stagger(200),
duration: 1200,
ease: "outElastic(1, .5)",
});
}, []);
return (
<div className="flex gap-8 mt-20 justify-center items-end min-h-[400px]">
<div className="stagger-item w-32 h-32 bg-red-400 rounded-lg shadow-2xl"></div>
<div className="stagger-item w-32 h-32 bg-green-400 rounded-lg shadow-2xl"></div>
<div className="stagger-item w-32 h-32 bg-blue-400 rounded-lg shadow-2xl"></div>
<div className="stagger-item w-32 h-32 bg-yellow-400 rounded-lg shadow-2xl"></div>
</div>
);
}
原文链接:https://code.ifrontend.net/archives/802,转载请注明出处。
评论0