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

React环境下anime.js时间线动态添加动画仅第一个播放如何解决?

问题修复方案

核心问题排查

  1. 局部变量didAnimate逻辑失效
    你将didAnimate声明为组件函数体内的普通let变量,组件每次重渲染都会将其重置为false,且useEffect闭包捕获的永远是当前渲染批次的变量值,动画执行标记完全不生效,会导致动画重复触发、被打断的问题。
    修复方式:将didAnimate改为ref存储:
const didAnimate = useRef(false);

执行完动画后设置didAnimate.current = true,判断条件改为!didAnimate.current。

  1. anime实例不该用state存储
    用useState存anime实例会触发不必要的组件重渲染,重渲染会导致DOM引用波动、动画被打断,应该改用useRef存储动画实例:
const animeRef = useRef(null);

赋值直接写animeRef.current = anime(/* 配置 */),不需要用setAnimeRef。

  1. complete回调写法错误
    你之前注释掉的complete回调是直接调用函数,而非传入函数引用:
// *错误写法*:动画初始化时就会执行该函数
complete: resetAnimationParams(_actorIdAnimating, _coordsFinal)
// *正确写法*:动画结束后才触发
complete: () => resetAnimationParams(_actorIdAnimating, _coordsFinal)

错误写法会在动画创建时就触发redux状态更新,导致组件重渲染,直接打断后续动画执行。

  1. 关键帧无属性变化导致视觉上无动画效果
    你给出的测试用例中,第一个关键帧结束时translateX已经是40,第二个关键帧的目标值还是translateX:40,没有属性变化,自然看不到动画效果,修改第二个关键帧的属性值即可验证。

  2. 动画位置衔接错误
    要实现动画从上一次结束位置开始,不要每次调用动画都用anime.set重置为固定的translateX: -23, translateY: -34,应该读取元素当前的transform值作为下一次动画的初始值,或者用变量存储上一次动画的最终坐标,直接复用为起点。


修复后的精简代码示例

import anime from "animejs";
import React, { useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import actions from "../../../../../../DataHandlers/redux/actions";
import animationsByDirection from "./animationsByDirection";
import styles from "./CharacterToken.module.css";

export default function CharacterToken(props) {
  const target = useRef();
  const animeRef = useRef(null);
  const didAnimate = useRef(false);
  const animationPath = useSelector((state) => state.UI.animationPath);
  const actorIdAnimating = useSelector((state) => state.UI.actorIdAnimating);
  const coordsFinal = [
    animationPath[animationPath.length - 1]?.x,
    animationPath[animationPath.length - 1]?.y,
  ];
  const dispatch = useDispatch();

  useEffect(() => {
    if (actorIdAnimating === props.actorHereId && !didAnimate.current) {
      animateMovement(animationPath, actorIdAnimating, coordsFinal);
      didAnimate.current = true;
    }
    // 可选:动画路径重置时重置didAnimate标记
    return () => {
      if(!animationPath?.length) didAnimate.current = false;
    }
  }, [actorIdAnimating, props.actorHereId, animationPath]);

  const resetAnimationParams = (_actorIdAnimating, _coordsFinal) => {
    dispatch(actions.setIsAnimatingtoCoords(undefined, undefined, undefined));
    dispatch(actions.moveActorLocationCombat(_actorIdAnimating, _coordsFinal));
  };

  function animateMovement(_animationPath, _actorIdAnimating, _coordsFinal) {
    const keyframes = []
    _animationPath.forEach((square) => {
      const deltaX = square.x - square.parent.x;
      const deltaY = square.y - square.parent.y;
      let direction;
      if (deltaX === -1 && deltaY === 0) direction = "north";
      else if (deltaX === 1 && deltaY === 0) direction = "south";
      else if (deltaX === 0 && deltaY === 1) direction = "east";
      else if (deltaX === 0 && deltaY === -1) direction = "west";
      else if (deltaX === -1 && deltaY === 1) direction = "northEast";
      else if (deltaX === -1 && deltaY === -1) direction = "northWest";
      else if (deltaX === 1 && deltaY === 1) direction = "southEast";
      else if (deltaX === 1 && deltaY === -1) direction = "southWest";
      keyframes.push(animationsByDirection[direction].keyframes[0])
    });
    // 仅首次初始化时重置初始位置,后续动画不要加这行
    if(!animeRef.current) {
      anime.set(target.current, { translateX: -23, translateY: -34, rotateZ: "315deg" });
    }
    animeRef.current = anime({
      targets: target.current,
      keyframes: keyframes,
      loop: false,
      easing: "easeOutExpo",
      duration: 750,
      complete: () => resetAnimationParams(_actorIdAnimating, _coordsFinal)
    })
  }
  
  return (
      <img
        ref={target}
        className={styles.CharacterToken}
        alt={""}
        src={props.tokenImage}
      />
  );
}

内容的提问来源于stack exchange,提问作者William Martin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 16:18:02