在这一节中,我们将自定义底部标签导航器按钮。具体实现步骤如下:
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 获取 playState 和 thumbnailUrl。
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. 动画控制
在 componentDidMount 和 componentDidUpdate 生命周期中,根据 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>
通过 currentTime 和 duration 计算进度百分比:
const percent = (currentTime / duration) * 100;
7. 总结
在本节中,我们实现了自定义底部标签按钮,动态显示播放状态和旋转动画,加入了圆形进度条来显示播放进度。下一节我们将实现一个独立于底部标签的播放按钮。