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

如何用Reanimated和HugeIcons实现图标触摸时平滑颜色切换

解决方案

你的问题在于直接通过isFocused切换颜色是瞬间变化,没有利用Reanimated的动画能力,且全局iconColor变量未和每个tab的状态关联。下面是修改后的代码,实现图标颜色从黑到白的平滑过渡:

关键修改点

  • 基于activeIndex为每个tab创建颜色插值动画,无需单独维护每个tab的状态
  • 使用interpolateColor实现RGB颜色的平滑过渡
  • 将HugeiconsIcon包装为Animated组件,支持动画化的color属性
import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withTiming,
  interpolate,
  interpolateColor,
  useAnimatedProps,
  createAnimatedComponent,
} from 'react-native-reanimated';
import { BaseButton } from '@react-native-material/core';
import { BottomTabBarProps } from '@react-navigation/bottom-tabs';
import { Home05Icon, WaterfallUp01Icon, Settings01Icon, HugeiconsIcon } from '@hugeicons/react-native';

// 将HugeiconsIcon转为支持动画的组件
const AnimatedHugeiconsIcon = createAnimatedComponent(HugeiconsIcon);

const SINGLE_TAB_WIDTH = 100; // 替换为你的单个tab实际宽度

function MyTabBar({ state, descriptors, navigation }: BottomTabBarProps) {
  const activeIndex = useSharedValue(state.index);

  React.useEffect(() => {
    activeIndex.value = withTiming(state.index, { duration: 200 });
  }, [activeIndex, state.index]);

  const slideAnimatedStyle = useAnimatedStyle(() => {
    return {
      transform: [{ translateX: activeIndex.value * SINGLE_TAB_WIDTH }],
    };
  });

  return (
    <View style={styles.container}>
      <Animated.View
        style={[
          styles.slide,
          slideAnimatedStyle,
        ]}
      />

      {state.routes.map((route, index) => {
        const { options } = descriptors[route.key];

        let iconName;
        if (route.name === 'HomeScreen') {
          iconName = Home05Icon;
        } else if (route.name === 'StatScreen') {
          iconName = WaterfallUp01Icon;
        } else if (route.name === 'SettingScreen') {
          iconName = Settings01Icon;
        }

        // 为当前tab生成动画化的颜色属性
        const animatedProps = useAnimatedProps(() => {
          // 计算当前tab与激活索引的差值,生成0-1的动画进度
          const progress = interpolate(activeIndex.value, [index - 0.5, index, index + 0.5], [0, 1, 0]);
          return {
            color: interpolateColor(progress, [0, 1], ['#000000', '#FFFFFF']),
          };
        });

        const onPress = () => {
          const event = navigation.emit({
            type: 'tabPress',
            target: route.key,
            canPreventDefault: true,
          });

          if (!state.index === index && !event.defaultPrevented) {
            navigation.navigate(route.name);
          }
        };

        return (
          <BaseButton
            key={index}
            onPress={onPress}
            rippleColor={'transparent'}
            style={{ width: SINGLE_TAB_WIDTH, alignItems: 'center', zIndex: 2 }}
          >
            <AnimatedHugeiconsIcon
              icon={iconName}
              size={22}
              animatedProps={animatedProps}
            />
          </BaseButton>
        );
      })}
    </View>
  );
}

// 自定义样式示例
const styles = {
  container: {
    flexDirection: 'row',
    backgroundColor: '#ffffff',
    borderTopWidth: 1,
    borderTopColor: '#eeeeee',
    height: 60,
    alignItems: 'center',
  },
  slide: {
    position: 'absolute',
    width: SINGLE_TAB_WIDTH,
    height: '100%',
    backgroundColor: '#165DFF', // 替换为你的tab激活背景色
    zIndex: 1,
  },
};

代码解释

  1. 动画组件包装:通过createAnimatedComponent将普通的HugeiconsIcon转为支持动画属性的组件,让color属性可以被Reanimated驱动。
  2. 颜色插值计算:在useAnimatedProps中,根据activeIndex的平滑变化,计算当前tab的动画进度,再用interpolateColor将进度映射为从黑色到白色的渐变颜色。
  3. 导航状态联动:当切换tab时,activeIndex通过withTiming做平滑过渡,自动带动所有tab的颜色动画同步执行。

修改后,切换tab时图标颜色会从黑色平滑过渡到白色,同时保留你原本的滑动指示器动画效果。

内容的提问来源于stack exchange,提问作者Thuận Dương Minh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 13:13:16