为什么要封装一个音频组件
主要因为微信小程序官方的audio
不维护了,并且在很多iOS
真机上确实也存在点击无法播放,总时长不显示等问题.
音频组件的要求与限制
- 点击播放或者暂停
- 显示播放进度及总时长
- 通过图标变化显示当前音频所处状态(暂停/播放/加载中)
- 页面音频更新时刷新组件状态
- 全局有且只有一个音频处于播放状态
- 离开页面之后要自动停止播放并销毁音频实例
材料:
icon_loading.gif
icon_playing.png
icon_paused.png
InnerAudioContext提供的属性和方法
属性:
string
src
: 音频资源的地址,用于直接播放。
bumberstartTime
: 开始播放的位置(单位:s),默认为 0
booleanautoplay
: 是否自动开始播放,默认为false
booleanloop
: 是否循环播放,默认为false
numbervolume
: 音量。范围 0~1。默认为 1
numberplaybackRate
: 播放速度。范围 0.5-2.0,默认为 1。(Android 需要 6 及以上版本)
numberduration
: 当前音频的长度(单位 s)。只有在当前有合法的 src 时返回(只读)
numbercurrentTime
: 当前音频的播放位置(单位 s)。只有在当前有合法的 src 时返回,时间保留小数点后 6 位(只读)
booleanpaused
: 当前是是否暂停或停止状态(只读)
numberbuffered
: 音频缓冲的时间点,仅保证当前播放时间点到此时间点内容已缓冲(只读)
方法:
play()
: 播放
pause()
: 暂停。暂停后的音频再播放会从暂停处开始播放
stop()
: 停止。停止后的音频再播放会从头开始播放。
seek(postions: number)
:跳转到指定位置
destory()
: 销毁当前实例
onCanplay(callback)
: 监听音频进入可以播放状态的事件。但不保证后面可以流畅播放
offCanplay(callback)
: 取消监听音频进入可以播放状态的事件
onPlay(callback)
: 监听音频播放事件
offPlay(callback)
: 取消监听音频播放事件
onPause(callback)
: 监听音频暂停事件
offPause(callback)
: 取消监听音频暂停事件
onStop(callback)
: 监听音频停止事件
offStop(callback)
: 取消监听音频停止事件
onEnded(callback)
: 监听音频自然播放至结束的事件
offEnded(callback)
: 取消监听音频自然播放至结束的事件
onTimeUpdate(callback)
: 监听音频播放进度更新事件
offTimeUpdate(callback)
: 取消监听音频播放进度更新事件
onError(callback)
: 监听音频播放错误事件
offError(callbcak)
: 取消监听音频播放错误事件
onWaiting(callback)
: 监听音频加载中事件。当音频因为数据不足,需要停下来加载时会触发
offWaiting(callback)
: 取消监听音频加载中事件
onSeeking(callback)
: 监听音频进行跳转操作的事件
offSeeking(callback)
: 取消监听音频进行跳转操作的事件
onSeeked(callback)
: 监听音频完成跳转操作的事件
offSeeked(callback)
: 取消监听音频完成跳转操作的事件
让我们开始吧🛠
Taro(React + TS)
- 首先构建一个简单的jsx结构:
<!-- playOrPauseAudio()是一个播放或者暂停播放音频的方法 -->
<!-- fmtSecond(time)是一个将秒格式化为 分:秒 的方法 -->
<View className='custom-audio'>
<Image onClick={() => this.playOrStopAudio()} src={audioImg} className='audio-btn' />
<Text>{this.fmtSecond(Math.floor(currentTime))}/{this.fmtSecond(Math.floor(duration))}</Text>
</View>
- 定义组件接受的参数
type PageOwnProps = {
audioSrc: string // 传入的音频的src
}
- 定义
CustomAudio
组件的初始化相关的操作,并给innerAudioContext
的回调添加一写行为
// src/components/widget/CustomAudio.tsx
import Taro, {
Component, ComponentClass } from '@tarojs/taro'
import {
View, Image, Text } from "@tarojs/components";
import iconPaused from '../../../assets/images/icon_paused.png'
import iconPlaying from '../../../assets/images/icon_playing.png'
import iconLoading from '../../../assets/images/icon_loading.gif'
interface StateInterface {
audioCtx: Taro.InnerAudioContext // innerAudioContext实例
audioImg: string // 当前音频icon标识
currentTime: number // 当前播放的时间
duration: number // 当前音频总时长
}
class CustomAudio extends Component<{
}, StateInterface> {
constructor(props) {
super(props)
this.fmtSecond = this.fmtSecond.bind(this)
this.state = {
audioCtx: Taro.createInnerAudioContext(),
audioImg: iconLoading, // 默认是在加载音频中的状态
currentTime: 0,
duration: 0
}
}
componentWillMount() {
const {
audioCtx,
audioImg
} = this.state
audioCtx.src = this.props.audioSrc
// 当播放的时候通过TimeUpdate的回调去更改当前播放时长和总时长(总时长更新放到onCanplay回调中会出错)
audioCtx.onTimeUpdate(() => {
if (audioCtx.currentTime > 0 && audioCtx.currentTime <= 1) {
this.setState({
currentTime: 1
})
} else if (audioCtx.currentTime !== Math.floor(audioCtx.currentTime)) {
this.setState({
currentTime: Math.floor(audioCtx.currentTime)
})
}
const tempDuration = Math.ceil(audioCtx.duration)
if (this.state.duration !== tempDuration) {
this.setState({
duration: tempDuration
})
}
console.log('onTimeUpdate')
})
// 当音频可以播放就将状态从loading变为可播放
audioCtx.onCanplay(() => {
if (audioImg === iconLoading) {
this.setAudioImg(iconPaused)
console.log('onCanplay')
}
})
// 当音频在缓冲时改变状态为加载中
audioCtx.onWaiting(() => {
if (audioImg !== iconLoading) {
this.setAudioImg(iconLoading)
}
})
// 开始播放后更改图标状态为播放中
audioCtx.