12-4 RN之暂停播放和显示播放时间

48 阅读2分钟

在上一节中,我们已经实现了音频播放功能,但未能实现暂停播放和显示播放时间。以下是实现这两个功能的步骤。

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}  // 自定义滑块
/>

总结

在本节中,我们实现了音频的暂停播放功能、显示播放时间和同步进度条。下一节将介绍如何实现上一首和下一首功能。