React JS中用Framer Motion实现Link组件悬停动画:完成后停止问题
解决方案
要实现鼠标离开Link组件时让当前动画完成后再停止,我们可以通过状态控制递归动画的方式替代repeat: Infinity,这样能精准控制动画在当前周期结束后终止。
修改后的完整代码
import { useState, useEffect, useRef } from 'react'; import { useAnimate, stagger } from 'framer-motion'; import SplitType from 'split-type'; const Link = ({ children, ...props }) => { const [scope, animate] = useAnimate(); const textRef = useRef(); const splitedText = useRef(null); const [isHovering, setIsHovering] = useState(false); useEffect(() => { splitedText.current = new SplitType(textRef.current, { tagName: "span", types: "words, chars", wordClass: "", charClass: "" }); return () => { if (splitedText.current) { splitedText.current.revert(); } }; }, [children]); const staggerMenuItems = stagger(0.1, { ease: "easeIn" }); const animateChars = async () => { if (!isHovering) return; await animate( splitedText.current.chars, { opacity: [1, 0.6, 1] }, { duration: 0.5, delay: staggerMenuItems } ); await new Promise(resolve => setTimeout(resolve, 600)); animateChars(); }; const handleMouseEnter = () => { setIsHovering(true); animateChars(); }; const handleMouseLeave = () => { setIsHovering(false); }; return ( <a ref={(el) => { textRef.current = el; scope.current = el; }} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} className="text-xl text-white" {...props} > {children} </a> ); }; export default Link;
关键修改说明
- 状态控制循环:用
isHovering状态替代repeat: Infinity,鼠标离开时将状态设为false,当前动画完成后自动停止递归,不会强制中断。 - 修复引用丢失:将
splitedText改为useRef存储,避免组件重渲染时丢失文本分割后的DOM引用。 - 动态内容适配:给
useEffect添加children依赖,确保链接内容变化时SplitType能重新分割文本。 - 内存泄漏防护:在
useEffect的清理函数中调用SplitType.revert(),组件卸载时还原原始DOM结构。 - 时序对齐:用
await等待动画完成和600ms延迟,完全对齐原代码中repeatDelay: 0.6的时序效果。
内容的提问来源于stack exchange,提问作者Amine Daimallah
相关产品推荐
相关产品推荐

