本文详解如何使用 useanimate 配合 setinterval,将多个连续动画步骤封装为可重复执行的无限循环序列,并确保内存安全与组件卸载时自动清理。
本文详解如何使用 useanimate 配合 setinterval,将多个连续动画步骤封装为可重复执行的无限循环序列,并确保内存安全与组件卸载时自动清理。
在 Framer Motion 中,useAnimate 提供了精细控制 DOM 元素动画的能力,特别适合构建多步、有依赖顺序的动画流程(如角色左右移动 + 镜像翻转模拟行走)。但默认情况下,runAnimation() 仅执行一次。要实现无缝、无限循环,关键在于:精确计算总时长、用 setInterval 触发重放、并通过 useRef 持久化定时器引用以支持安全清理。
以下是一个完整、生产就绪的实现示例:
import { motion, useAnimate } from "framer-motion";
import { useEffect, useRef } from "react";
export default function PacmanLoop() {
const [scope, animate] = useAnimate();
// 封装动画序列:右移 → 水平翻转 → 左移 → 恢复方向
const runAnimation = async () => {
await animate(scope.current, { x: 300 }, { duration: 2 }); // 向右滑动 2s
await animate(scope.current, { scaleX: -1 }, { duration: 0.2 }); // 瞬间镜像(0.2s)
await animate(scope.current, { x: -200 }, { duration: 2 }); // 向左滑回 2s
await animate(scope.current, { scaleX: 1 }, { duration: 0.2 }); // 瞬间复位
};
// 使用 useRef 持久化 setInterval 返回值,避免闭包捕获过期引用
const intervalRef = useRef<NodeJS.Timeout | null>(null);
const startLoop = () => {
// ⚠️ 注意:总时长必须严格等于所有动画 duration 之和(含延迟)
// 此处:2 + 0.2 + 2 + 0.2 = 4.4 秒 → 4400ms
intervalRef.current = setInterval(() => {
runAnimation();
}, 4400);
};
useEffect(() => {
startLoop();
// ✅ 组件卸载时自动清除定时器,防止内存泄漏和状态更新错误
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, []); // 依赖数组为空,仅在挂载/卸载时运行
return (
<motion.img
ref={scope}
initial={{ x: -200 }}
alt="pacman"
className="absolute z-50"
src="/pacman.gif"
width={100}
height={50}
/>
);
}关键要点说明:
- 时长同步是循环流畅的核心:setInterval 的间隔必须严格等于动画序列总耗时(本例为 4400ms)。若动画中存在 delay、stagger 或动态时长,需统一纳入计算。
- useRef 而非 useState 存储定时器:setInterval 返回的 timeoutID 是不可变标识,useRef 可跨渲染周期稳定访问,避免 useEffect 闭包中引用过期导致 clearInterval 失效。
- 务必清理副作用:useEffect 的返回函数是 React 官方推荐的清理机制,遗漏将导致定时器持续运行,引发内存泄漏或在已卸载组件上调用 animate() 报错。
-
进阶优化建议:
- 若需暂停/恢复循环,可将 intervalRef 与 useState<boolean> 结合,通过 clearInterval / startLoop 控制;
- 对高帧率敏感场景,可改用 requestAnimationFrame + 时间戳手动调度,但本例中 setInterval 简洁可靠,且 4.4s 周期对性能无压力。
通过以上模式,你不仅能实现 Pac-Man 循环跑动这类经典效果,还可扩展至任何多阶段、需反复执行的交互动画逻辑——简洁、可控、符合 React 最佳实践。










