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

React Native Animated.sequence调用stop后start回调未立即触发问题

问题分析与解决方案

核心问题

你当前的操作错误在于:每次触发useEffect时都会创建新的Animated.sequence实例,调用animation.stop()的是刚创建的新动画对象,而非正在运行的旧动画,导致旧动画完全没被终止,它的回调仍会在原序列结束后执行,进而提前隐藏新Toast。同时animationRunning.current的逻辑存在漏洞,新动画启动后会覆盖标记,但旧动画的回调依然会在结束时修改标记并触发endAnimation。


修正方案1:跟踪并终止运行中的动画实例

把当前正在运行的动画实例存在Ref中,确保每次触发新动画时能精准终止旧动画,同时重置动画状态避免异常:

import { useRef, useEffect } from 'react';
import { Animated } from 'react-native';

const Toast = ({ message, endAnimation, duration = 1500 }) => {
  const fadeAnim = useRef(new Animated.Value(0)).current;
  const scaleAnim = useRef(new Animated.Value(1)).current;
  const currentAnimation = useRef(null); // 保存当前运行的动画实例

  useEffect(() => {
    // 终止旧动画并重置状态
    if (currentAnimation.current) {
      currentAnimation.current.stop();
      // 立即重置动画到初始值,避免新动画起始状态异常
      Animated.parallel([
        Animated.timing(fadeAnim, { toValue: 0, duration: 0, useNativeDriver: true }),
        Animated.timing(scaleAnim, { toValue: 1, duration: 0, useNativeDriver: true })
      ]).start();
    }

    // 创建新动画序列
    const animation = Animated.sequence([
      Animated.timing(fadeAnim, {
        duration: 300,
        toValue: 1,
        useNativeDriver: true,
      }),
      Animated.timing(scaleAnim, {
        duration: 300,
        toValue: 1.1,
        useNativeDriver: true,
      }),
      Animated.timing(scaleAnim, {
        duration: 300,
        toValue: 1,
        useNativeDriver: true,
      }),
      Animated.delay(duration),
      Animated.timing(fadeAnim, {
        duration: 300,
        toValue: 0,
        useNativeDriver: true,
      }),
    ]);

    currentAnimation.current = animation;
    animation.start(({ finished }) => {
      currentAnimation.current = null;
      // 仅当动画正常完成时触发回调,避免终止时误执行
      if (finished) {
        endAnimation();
      }
    });

    // 组件卸载时清理动画
    return () => {
      if (currentAnimation.current) {
        currentAnimation.current.stop();
      }
    };
  }, [message, duration, endAnimation]);

  // 你的Toast渲染逻辑
  return (
    <Animated.View style={{ opacity: fadeAnim, transform: [{ scale: scaleAnim }] }}>
      <Text>{message}</Text>
    </Animated.View>
  );
};

修正方案2:标记有效动画,屏蔽旧回调干扰

如果担心动画终止时的状态同步问题,可以用唯一ID标记当前动画,旧动画的回调触发时直接忽略:

const Toast = ({ message, endAnimation, duration = 1500 }) => {
  const fadeAnim = useRef(new Animated.Value(0)).current;
  const scaleAnim = useRef(new Animated.Value(1)).current;
  const animationId = useRef(0); // 标记当前动画的唯一ID

  useEffect(() => {
    const currentId = ++animationId.current;

    // 重置动画到初始状态
    Animated.parallel([
      Animated.timing(fadeAnim, { toValue: 0, duration: 0, useNativeDriver: true }),
      Animated.timing(scaleAnim, { toValue: 1, duration: 0, useNativeDriver: true })
    ]).start(() => {
      const animation = Animated.sequence([
        // 动画序列和之前一致
        Animated.timing(fadeAnim, { duration: 300, toValue: 1, useNativeDriver: true }),
        Animated.timing(scaleAnim, { duration: 300, toValue: 1.1, useNativeDriver: true }),
        Animated.timing(scaleAnim, { duration: 300, toValue: 1, useNativeDriver: true }),
        Animated.delay(duration),
        Animated.timing(fadeAnim, { duration: 300, toValue: 0, useNativeDriver: true }),
      ]);

      animation.start(() => {
        // 仅当当前动画是最新的才执行回调
        if (animationId.current === currentId) {
          endAnimation();
        }
      });
    });

    return () => {
      animationId.current = 0; // 组件卸载时标记为无效
    };
  }, [message, duration, endAnimation]);

  // 渲染逻辑...
};

父组件状态简化(可选)

可以合并message和showToast状态,避免状态不同步问题:

const [toastMessage, setToastMessage] = useState('');

const onInfoCopied = (value) => {
  setToastMessage(value);
};

// 渲染Toast
{toastMessage && (
  <Toast 
    message={`${toastMessage} copied`} 
    endAnimation={() => setToastMessage('')} 
  />
)}

内容的提问来源于stack exchange,提问作者Simple Simon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 09:02:56