在上一节中,我们已经实现了音频播放功能,但未能实现暂停播放和显示播放时间。以下是实现这两个功能的步骤。
1. 添加暂停功能
首先,我们需要在 model 中添加暂停音频的逻辑。
*pause(_, { put, select }) {
// 停止播放
yield call(pause);
// 更新播放状态为暂停
yield put({
type: 'setState',
payload: {
playState: 'paused',
},
});
}
接着,在页面中添加暂停/播放按钮。当音频处于播放状态时,点击按钮暂停播放;再次点击按钮恢复播放。
<View style={styles.container}>
<Touchable onPress={this.toggle} disabled={playState}>
<Icon name={playState === 'playing' ? "icon-paste" : 'icon-bofang'} size={40} color="#fff" />
</Touchable>
</View>
toggle = () => {
const { dispatch, playState } = this.props;
dispatch({
type: playState === 'playing' ? 'player/pause' : 'player/play',
});
};
const styles = StyleSheet.create({
container: {
paddingTop: 100,
},
});
2. 添加进度条显示播放时间
为了显示音频播放进度,使用 react-native-slider-x 库来实现音频进度条。
安装库:
yarn add react-native-slider-x
创建 PlaySlider.tsx 文件,初始化进度条显示。
const { duration } = this.props;
<View style={styles.container}>
<Slider
value={0}
maximumValue={duration < 0 ? 100 : duration}
maximumTrackTintColor="rgba(255, 255, 255, 0.3)"
minimumTrackTintColor="white"
/>
</View>
const styles = StyleSheet.create({
container: {
margin: 15,
},
thumb: {
width: 76,
height: 20,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
text: {
fontSize: 10,
},
});
3. 实现播放时间与进度条同步
为了保持进度条与音频播放时间同步,需要使用定时器功能,每秒获取一次播放时间。我们通过 DVA 中的 effect 来实现轮询。
export interface PlayerModelType extends Model {
effects: {
watcherCurrentTime: EffectWithType;
};
}
watcherCurrentTime: [
function* (sagaEffects) {
const { call, take, race } = sagaEffects;
while (true) {
// 监听 play 事件
yield take('play');
yield race([call(currentTime, sagaEffects), take('pause')]);
}
},
{ type: 'watcher' },
];
定义 currentTime 函数,每隔一秒获取当前播放时间。
function* currentTime({ call, put }: EffectsCommandMap) {
while (true) {
yield call(delay, 1000);
const currentTime = yield call(getCurrentTime);
yield put({
type: 'setCurrentTime',
payload: { currentTime },
});
}
}
const delay = (timeout: number) =>
new Promise(resolve => setTimeout(resolve, timeout));
4. 更新进度条显示
在 PlaySlider.tsx 中,绑定播放时间与进度条的值。
const { currentTime } = this.props;
<View style={styles.container}>
<Slider
value={currentTime}
thumbTintColor="white"
thumbStyle={styles.thumb}
renderThumb={this.renderThumb} // 自定义滑块
/>
</View>
renderThumb = () => {
const { duration, currentTime } = this.props;
const currentTimeString = formatTime(currentTime);
const durationString = formatTime(duration);
return (
<View>
<Text style={styles.text}>
{currentTimeString}/{durationString}
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
margin: 10,
},
thumb: {
justifyContent: 'center',
alignItems: 'center',
width: 76,
height: 20,
},
text: {
fontSize: 10,
},
});
function formatTime(seconds: number) {
const m = parseInt((seconds % (60 * 60)) / 60 + '', 10);
const s = parseInt((seconds % 60) + '', 10);
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
}
5. 解决播放结束问题
在音频播放完毕后,play 函数可能会出错。原因是播放完成后,我们需要保持音频资源而不是释放它。因此,修改 play 函数,避免在播放完成后释放资源。
6. 自定义滑块
根据设计图需求,自定义进度条滑块为显示时间的圆角矩形,显示当前播放时间和音频总时长。
<Slider
value={currentTime}
thumbTintColor="white"
thumbStyle={styles.thumb}
renderThumb={this.renderThumb} // 自定义滑块
/>
总结
在本节中,我们实现了音频的暂停播放功能、显示播放时间和同步进度条。下一节将介绍如何实现上一首和下一首功能。