You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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;

关键修改点

  1. 鼠标离开逻辑优化:将kill()改为设置repeat(0),GSAP会在完成当前循环周期后停止动画,而非立即终止。
  2. 内存泄漏修复:新增splitTextRef存储SplitType实例,并在useEffect的返回函数中调用destroy()清理资源。
  3. 类型规范:完善了TypeScript类型定义,代码更严谨。

内容的提问来源于stack exchange,提问作者Amine Daimallah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 22:05:55