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

Class组件转Function组件:动画元素无法保持位置的问题

问题:Function组件动画结束后元素回到初始位置

我有一个可正常运行的Class组件,将其转换为Function组件后,图片完成移动动画无法停留在目标位置,会回到初始创建位置。以下是问题代码及修复方案:


问题原因分析

  • Animated.Value未持久化:Function组件中positionX/positionY直接声明,每次组件重渲染都会重新初始化,动画完成后一旦组件刷新,就会回到初始值0;而Class组件将这些值存在state中,生命周期内不会被重置。
  • 状态更新依赖旧值:addDog直接使用dogs数组拼接,多次点击时会因闭包捕获旧状态导致更新异常,新添加的元素可能无法正确渲染。
  • 未传递外部样式:DogContainer未使用传入的style props,硬编码bottom:125,与Class组件逻辑不一致。
  • 缺少动画完成回调:原Class组件的onComplete移除逻辑在Function组件中未实现。

修复后的Function组件代码

import React, { useState, useEffect, useRef } from 'react';
import { View, TouchableOpacity, Text, Animated, Image, Easing } from 'react-native';

// 假设width和dogImg已提前定义
let dogCount = 0;

export default function App() {
  const [dogs, setDogs] = useState([]);

  const addDog = () => {
    // 使用函数式更新获取最新状态,避免闭包问题
    setDogs(prevDogs => {
      const newDog = { id: dogCount, bottom: 125 };
      dogCount++;
      return [...prevDogs, newDog];
    });
  };

  const removeDog = (id) => {
    setDogs(prevDogs => prevDogs.filter(dog => dog.id !== id));
  };

  return (
    <View style={styles.entirebottomsheet}>
      <View style={styles.container}>
        <TouchableOpacity onPress={addDog} style={styles.dogButton}>
          <Text>Dog</Text>
        </TouchableOpacity>
        {dogs.map(dog => (
          <DogContainer
            key={dog.id}
            style={{ bottom: dog.bottom, position: 'absolute' }}
            onComplete={() => removeDog(dog.id)}
          />
        ))}
      </View>
    </View>
  );
}

function DogContainer({ style, onComplete }) {
  // 用useRef持久化Animated.Value,避免重渲染时重置
  const positionX = useRef(new Animated.Value(0)).current;
  const positionY = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    const animations = [
      Animated.spring(positionX, {
        duration: 1000,
        toValue: width / 3,
        easing: Easing.ease,
        useNativeDriver: true,
      }),
      Animated.spring(positionY, {
        duration: 1000,
        toValue: -340, // 和Class组件保持一致的目标值
        easing: Easing.ease,
        useNativeDriver: true,
      })
    ];

    // 并行执行动画,结束后调用回调
    Animated.parallel(animations).start(() => {
      console.log('finish');
      onComplete?.();
    });

    // 组件卸载时停止动画,避免内存泄漏
    return () => {
      positionX.stopAnimation();
      positionY.stopAnimation();
    };
  }, [positionX, positionY, onComplete]);

  return (
    <Animated.View 
      style={[
        style, // 应用外部传入的样式
        {
          transform: [{ translateX: positionX }, { translateY: positionY }],
        }
      ]}
    >
      <Image source={dogImg} style={{ width: 60, height: 60, borderRadius: 30 }} />
    </Animated.View>
  );
}

// 假设样式已定义
const styles = {
  entirebottomsheet: { /* 你的样式 */ },
  container: { /* 你的样式 */ },
  dogButton: { /* 你的样式 */ }
};

关键修复点

  1. useRef持久化动画值:确保组件重渲染时不会重置动画状态,保持动画后的位置。
  2. 函数式状态更新:使用setDogs(prevDogs => ...)获取最新状态,避免闭包导致的旧值问题。
  3. 传递并合并外部样式:DogContainer接收并应用传入的style props,与Class组件逻辑对齐。
  4. 实现动画完成回调:用Animated.parallel并行执行动画,结束后调用onComplete处理移除逻辑。
  5. 添加动画清理:组件卸载时停止动画,避免内存泄漏。

内容的提问来源于stack exchange,提问作者이수민

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 07:10:30