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

如何用Reanimated 3.4.1实现View平滑扩宽并带动后续元素动画

解决方案

直接动画flex属性无法实现平滑过渡,因为flex是布局计算的权重参数,并非可直接插值的样式属性。要实现你要的效果,需要通过动态计算目标宽度+动画宽度属性来实现,同时利用Flex布局的自然特性带动后续元素移动。

核心思路

  1. 先获取父容器的总宽度,减去#1和#3的固定/动态宽度,得到#2的目标填充宽度
  2. 使用Reanimated的共享值(Shared Value)控制#2的宽度,通过withTiming实现从0到目标宽度的平滑过渡
  3. 利用Flex布局的流式排列特性,#2宽度扩展时会自然推动#3,无需额外为#3做动画

完整代码示例

import React, { useLayoutEffect } from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import Animated, { 
  useSharedValue, 
  useAnimatedStyle, 
  withTiming, 
  useAnimatedRef, 
  measure 
} from 'react-native-reanimated';

const ExpandableRow = () => {
  const containerRef = useAnimatedRef();
  const box1Ref = useAnimatedRef();
  const box3Ref = useAnimatedRef();
  const box2Width = useSharedValue(0);
  let targetWidth = 0;

  // 计算#2的目标宽度:父容器宽度 - #1宽度 - #3宽度
  useLayoutEffect(() => {
    const calculateTargetWidth = async () => {
      const containerLayout = await measure(containerRef);
      const box1Layout = await measure(box1Ref);
      const box3Layout = await measure(box3Ref);
      targetWidth = containerLayout.width - box1Layout.width - box3Layout.width;
    };
    calculateTargetWidth();
  }, []);

  // 绑定动画样式
  const box2AnimatedStyle = useAnimatedStyle(() => ({
    width: box2Width.value,
  }));

  // 启动展开动画
  const expandBox2 = () => {
    box2Width.value = withTiming(targetWidth, { duration: 600 });
  };

  // 启动收起动画
  const collapseBox2 = () => {
    box2Width.value = withTiming(0, { duration: 600 });
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', paddingHorizontal: 20 }}>
      {/* 父容器:横向排列 */}
      <Animated.View 
        ref={containerRef}
        style={{ 
          flexDirection: 'row', 
          alignItems: 'center', 
          height: 50, 
          backgroundColor: '#f5f5f5',
          borderRadius: 8
        }}
      >
        {/* #1:固定宽度元素 */}
        <Animated.View 
          ref={box1Ref}
          style={{ 
            width: 80, 
            height: '100%', 
            backgroundColor: '#ff6b6b',
            justifyContent: 'center',
            alignItems: 'center',
            borderTopLeftRadius: 8,
            borderBottomLeftRadius: 8
          }}
        >
          <Text style={{ color: '#fff', fontWeight: 'bold' }}>#1</Text>
        </Animated.View>

        {/* #2:动画宽度元素 */}
        <Animated.View 
          style={[
            box2AnimatedStyle,
            { 
              height: '100%', 
              backgroundColor: '#4ecdc4',
              justifyContent: 'center',
              alignItems: 'center'
            }
          ]}
        >
          <Text style={{ color: '#fff', fontWeight: 'bold' }}>#2</Text>
        </Animated.View>

        {/* #3:固定宽度元素 */}
        <Animated.View 
          ref={box3Ref}
          style={{ 
            width: 80, 
            height: '100%', 
            backgroundColor: '#45b7d1',
            justifyContent: 'center',
            alignItems: 'center',
            borderTopRightRadius: 8,
            borderBottomRightRadius: 8
          }}
        >
          <Text style={{ color: '#fff', fontWeight: 'bold' }}>#3</Text>
        </Animated.View>
      </Animated.View>

      {/* 控制按钮 */}
      <View style={{ flexDirection: 'row', gap: 12, marginTop: 24 }}>
        <TouchableOpacity 
          onPress={expandBox2}
          style={{ 
            flex: 1, 
            padding: 12, 
            backgroundColor: '#2ecc71', 
            borderRadius: 8 
          }}
        >
          <Text style={{ color: '#fff', textAlign: 'center', fontWeight: 'bold' }}>展开#2</Text>
        </TouchableOpacity>
        <TouchableOpacity 
          onPress={collapseBox2}
          style={{ 
            flex: 1, 
            padding: 12, 
            backgroundColor: '#e74c3c', 
            borderRadius: 8 
          }}
        >
          <Text style={{ color: '#fff', textAlign: 'center', fontWeight: 'bold' }}>收起#2</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
};

export default ExpandableRow;

关键细节说明

  • 尺寸测量:使用useAnimatedRef和measure API获取元素尺寸,确保在Reanimated的上下文里异步获取准确的布局信息
  • 动画控制:通过useAnimatedStyle将共享值绑定到宽度样式,withTiming提供平滑的过渡曲线
  • 后续元素动画:由于父容器是row方向的Flex布局,#2宽度扩展时会自动挤压#3的位置,#3会自然获得平滑的移动动画,无需额外处理
  • 动态适配:如果#1或#3的宽度是动态变化的(比如文本长度变化),可以用useAnimatedReaction监听它们的尺寸变化,实时更新targetWidth并调整动画值

内容的提问来源于stack exchange,提问作者Can Poyrazoğlu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 10:33:15