13-2 RN之自定义底部标签按钮

49 阅读2分钟

在这一节中,我们将自定义底部标签导航器按钮。具体实现步骤如下:

1. 自定义底部标签按钮

首先,打开 navigator/BottomTabs.tsx,在标签导航器中,使用 tabBarButton 来自定义底部标签按钮。

<Tab.Screen
  name="Pay"
  options={({navigation}) => ({
    tabBarButton: (props: BottomTabBarButtonProps) => {
      return (
        // 返回自定义标签
      );
    },
  })}
/>

接着,在 pages/view 文件夹下创建 Play 组件,并实现按钮的样式和图标。

import React from 'react';
import Touchable from '@/components/Touchable';
import Icon from '@/assets/iconfont/index';

class Play extends React.Component {
  render() {
    return (
      <Touchable style={styles.play}>
        <Icon name="icon-bofang3" color="#ededed" size={40} />
      </Touchable>
    );
  }
}

const styles = StyleSheet.create({
  play: {
    borderRadius: 21,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

export default Play;

2. 获取播放状态和频道图片

为了在按钮中显示当前播放的图片和状态,需要连接到 player 的 model。通过 mapStateToProps 获取 playStatethumbnailUrl

const mapStateToProps = ({ player }: RootState) => ({
  playState: player.playState,
  thumbnailUrl: player.thumbnailUrl,
});

const connector = connect(mapStateToProps);

type ModelState = ConnectedProps<typeof connector>;

interface IProps extends ModelState {
  percent: number;
}

connector(Play)

render 函数中,判断是否有 thumbnailUrl,如果有则显示图片,否则显示图标。

const { thumbnailUrl } = this.props;
{thumbnailUrl ? (
  <Image source={{ uri: thumbnailUrl }} style={styles.image} />
) : (
  <Icon name="icon-bofang3" color="#ededed" size={40} />
)}

const styles = StyleSheet.create({
  image: {
    width: 42,
    height: 42,
  },
});

3. 添加旋转动画

为了让图片旋转,我们使用 Animated.Value 创建动画,并通过 interpolate 实现旋转效果。

anim = new Animated.Value(0);

constructor(props: IProps) {
  super(props);
  this.spin = Animated.timing(this.anim, {
    toValue: 1,
    duration: 10000,
    easing: Easing.linear,
    useNativeDriver: true,
  }).start();
}

const rotate = this.anim.interpolate({
  inputRange: [0, 1],
  outputRange: ['0deg', '360deg'],
});

<Animated.View style={{ transform: [{ rotate }] }}>
  {thumbnailUrl ? (
    <Image source={{ uri: thumbnailUrl }} style={styles.image} />
  ) : (
    <Icon name="icon-bofang3" color="#ededed" size={40} />
  )}
</Animated.View>

4. 循环动画

为了让图片旋转无限循环,我们将动画包裹在 Animated.loop 中。

this.spin = Animated.loop(
  Animated.timing(this.anim, {
    toValue: 1,
    duration: 10000,
    easing: Easing.linear,
    useNativeDriver: true,
  }),
  { iterations: -1 },
);

5. 动画控制

componentDidMountcomponentDidUpdate 生命周期中,根据 playState 控制动画的开始和停止。

componentDidMount() {
  const { playState } = this.props;
  if (playState === 'playing') {
    this.spin.start();
  }
}

componentDidUpdate() {
  const { playState } = this.props;
  if (playState === 'playing') {
    this.spin.start();
  } else {
    this.spin.stop();
  }
}

6. 添加圆形进度条

为了展示播放进度,我们使用第三方库 react-native-circular-progress 来实现圆形进度条。

yarn add react-native-circular-progress

pages/view 下创建 Progress 组件:

import { AnimatedCircularProgress } from 'react-native-circular-progress';

<AnimatedCircularProgress
  size={40}
  width={2}
  fill={percent}
  tintColor="#f86442"
  backgroundColor="#ededed">
  {() => <>{children}</>}
</AnimatedCircularProgress>

通过 currentTimeduration 计算进度百分比:

const percent = (currentTime / duration) * 100;

7. 总结

在本节中,我们实现了自定义底部标签按钮,动态显示播放状态和旋转动画,加入了圆形进度条来显示播放进度。下一节我们将实现一个独立于底部标签的播放按钮。