基于React+GSAP的链接文本悬停闪光动画实现问题
React + GSAP 链接组件:实现悬停循环动画、离开时完成当前周期再停止
需求说明
- 鼠标悬停时,启动字符透明度波动动画并持续循环播放
- 鼠标离开时,等待动画完成当前完整周期后再停止,而非立即终止
原代码问题分析
当前handleMouseLeave方法中使用tl.current.resume().kill()会直接终止动画,无法满足“完成当前周期再停止”的需求。此外,未对SplitType实例进行清理,存在内存泄漏风险。
修正后的完整代码
import { forwardRef, useEffect, useRef } from 'react'; import gsap from 'gsap'; import SplitType from 'split-type'; type LinkProps = { children: React.ReactNode; variant?: string; } & React.AnchorHTMLAttributes<HTMLAnchorElement>; const Link = forwardRef<HTMLAnchorElement, LinkProps>(({ children, variant, ...props }, ref) => { const textRef = useRef<HTMLAnchorElement>(null); const tl = useRef<gsap.core.Timeline>(); const splitTextRef = useRef<SplitType>(); useEffect(() => { if (textRef.current) { splitTextRef.current = new SplitType(textRef.current, { tagName: "span", types: "words, chars", wordClass: "", charClass: "", }); const chars = splitTextRef.current.chars; tl.current = gsap.timeline({ paused: true, }); tl.current .to(chars, { opacity: 0.2, ease: "power2.inOut", stagger: { amount: 0.4, ease: "none", }, }) .to( chars, { opacity: 1, ease: "power2.inOut", stagger: { amount: 0.4, ease: "none", }, }, "<+=0.3" ); } // 清理SplitType实例,避免内存泄漏 return () => { splitTextRef.current?.destroy(); }; }, []); const handleMouseEnter = () => { tl.current?.restart().repeat(-1); }; const handleMouseLeave = () => { if (tl.current) { // 将重复次数设为0,动画会完成当前周期后自动停止 tl.current.repeat(0); // 确保动画继续播放(若当前处于暂停状态) tl.current.play(); } }; return ( <a ref={textRef} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} className={cn(linkStyle({ variant }))} {...props} > {children} </a> ); }); export default Link;
关键修改点
- 鼠标离开逻辑优化:将
kill()改为设置repeat(0),GSAP会在完成当前循环周期后停止动画,而非立即终止。 - 内存泄漏修复:新增
splitTextRef存储SplitType实例,并在useEffect的返回函数中调用destroy()清理资源。 - 类型规范:完善了TypeScript类型定义,代码更严谨。
内容的提问来源于stack exchange,提问作者Amine Daimallah
相关产品推荐
相关产品推荐

