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

React Native中Animated与Reanimated兼容及代码转换咨询

转换原生Animated代码到Reanimated的方案

你不需要从零重写,完全可以基于现有逻辑转换为Reanimated兼容版本。Reanimated v2+虽然API和原生Animated差异较大,但可以完美适配你的列表项缩放需求,且动画性能更优。

核心差异说明

你之前报错的主要原因是混淆了原生Animated和Reanimated的API:

  • 原生Animated.Value对应Reanimated的useSharedValue(UI线程管理的动画状态)
  • 原生动画序列Animated.sequence对应Reanimated的withSequence
  • 原生的插值写法value.interpolate()需要放在useAnimatedStyle中执行
  • Reanimated的动画不需要调用start(),直接赋值给sharedValue即可

转换后的完整代码

import React, { useState } from 'react';
import {
  Text,
  View,
  StyleSheet,
  FlatList,
  TouchableOpacity,
} from 'react-native';
import Constants from 'expo-constants';
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
  withSequence,
  runOnJS,
} from 'react-native-reanimated';

const dummyData = [...Array(10)].map((_, i) => ({
  title: `title ${i}`,
  id: i,
}));

const App = () => {
  const [data, setData] = useState(dummyData);
  const [activeItem, setActiveItem] = useState(null);

  // 替换原生Animated.Value为Reanimated共享值
  const animateValue = useSharedValue(0);

  const renderItem = ({ item, index }) => {
    const animate = () => {
      setActiveItem(index);
      // 执行缩放序列动画:0→1→0
      animateValue.value = withSequence(
        withSpring(1),
        withSpring(0, undefined, (finished) => {
          if (finished) {
            // 跨线程执行JS回调
            runOnJS(console.log)('animation finished');
          }
        })
      );
    };

    // 定义UI线程计算的动画样式
    const animatedStyle = useAnimatedStyle(() => {
      const scale = animateValue.value.interpolate({
        inputRange: [0, 1],
        outputRange: [1, 1.5],
      });
      return {
        transform: [{ scale }],
      };
    });

    return (
      <TouchableOpacity onPress={animate}>
        {activeItem === index ? (
          <Animated.View style={[styles.button, animatedStyle]}>
            <Text>{item.title}</Text>
          </Animated.View>
        ) : (
          <View style={styles.button}>
            <Text>{item.title}</Text>
          </View>
        )}
      </TouchableOpacity>
    );
  };

  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={({ id }) => id.toString()}
        extraData={activeItem}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
  button: {
    padding: 10,
    borderWidth: 1,
    alignItems: 'center',
    backgroundColor: 'skyblue',
  },
});

export default App;

优化建议:独立项动画

如果想避免快速点击不同项时的动画冲突,可以让每个列表项维护自己的动画状态,去掉全局的activeItem和animateValue,代码更简洁健壮:

const renderItem = ({ item }) => {
  // 每个项独立的缩放状态
  const scale = useSharedValue(1);

  const animate = () => {
    scale.value = withSequence(
      withSpring(1.5),
      withSpring(1, undefined, (finished) => {
        if (finished) runOnJS(console.log)('animation finished');
      })
    );
  };

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
  }));

  return (
    <TouchableOpacity onPress={animate}>
      <Animated.View style={[styles.button, animatedStyle]}>
        <Text>{item.title}</Text>
      </Animated.View>
    </TouchableOpacity>
  );
};

注意事项

  • 只从react-native-reanimated导入Animated,不要同时导入原生Animated,避免API冲突
  • Reanimated的动画回调需要用runOnJS包裹,确保在JS线程执行
  • useAnimatedStyle内部的代码会在UI线程运行,不要在这里执行JS线程的逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 22:45:37