起初,音频肯定是先想到InnerAudioContext,但因为项目需要嵌套在腾讯随行app里面,所以这里需要调用的是背景音频getBackgroundAudioManager,思路是差不多的。
const audioContext = Taro.getBackgroundAudioManager();
let timer = null
interface IState {
isAnimation: boolean;
duration: number;
audioCurrent: number;
progressPrcent: number
}
export default class List extends Component<IProps, IState> {
constructor(props: IProps | undefined) {
super(props);
this.state = {
isAnimation: false,
duration: 0,
audioCurrent: 0,
progressPrcent: 0
};
}
componentWillUnmount() {
wx.stopBackgroundAudio()
}
componentDidMount() {
this.getScenicDetail()
}
getScenicDetail() {
this.props.dispatch({
type: 'home/getScenicDetail',
payload: {
scenicId: this.$router.params.id
},
callback: (info) => {
this.setState({
scenicInfo: info
}, () => {
this.initialization(info.voiceFile, info.name)
})
}
})
}
//初始化播放器,获取duration
initialization(src, name) {
if (src) {
const self = this;
//设置src
audioContext.src = src && (src.indexOf('https:') > -1 ? src : buildCdnPath(src))
//运行一次
audioContext.title = name;
audioContext.play()
// audioContext.pause() //因为需求是要求页面初始化就自动播放,所以这里没有执行pause
audioContext.onPlay(() => {
//开始播放事件
timer = setTimeout(function() {
//延时获取音频真正的duration
console.log(audioContext.duration)
// 这个判断是防止多个音频切换时,图标未显示暂停状态
if (audioContext.duration === 0 || audioContext.duration === null) {
self.setState({
isAnimation: false
});
} else {
self.setState({
isAnimation: true,
duration: audioContext.duration
});
}
}, 500)
});
audioContext.onTimeUpdate(() => {
// 这个if是防止为获取到时长时,图标显示的问题;如果不存在这bug,直接执行else里面的代码就行
if (this.state.duration === null || this.state.duration === 0 ) {
this.setState({
progressPrcent: (audioContext.currentTime / audioContext.duration) * 100,
audioCurrent: audioContext.currentTime,
duration: audioContext.duration,
isAnimation: audioContext.duration > 0
});
} else {
this.setState({
progressPrcent:
(audioContext.currentTime / audioContext.duration) * 100,
audioCurrent: audioContext.currentTime
});
}
})
audioContext.onEnded(() => {
//自然播放结束事件
this.setState({
isAnimation: false,
progressPrcent: 0,
audioCurrent: 0,
duration: this.state.duration
});
});
//监听播放错误事件
audioContext.onError(() => {
// wx.stopBackgroundAudio() // 注意这里不需要暂停画蛇添足
this.setState({
isAnimation: false,
progressPrcent: 0,
audioCurrent: 0
});
});
} else {
// wx.stopBackgroundAudio() // 注意这里不需要暂停画蛇添足
audioContext.pause()
Taro.showToast({title: '该景点无语音播放地址', icon: 'none'})
this.setState({
isAnimation: false,
duration: 0,
progressPrcent: 0,
audioCurrent: 0
});
}
}
// 播放和暂停
playAudio(src, name) {
if (!src) {
Taro.showToast({title: '该景点无语音播放地址', icon: 'none'})
return
}
if (!this.state.isAnimation) {
this.setState({
isAnimation: true
})
if (audioContext.duration === 0) {
audioContext.src = src && (src.indexOf('https:') > -1 ? src : buildCdnPath(src))
audioContext.title = name
}
if (this.state.progressPrcent !== 0) {
audioContext.seek((this.state.progressPrcent / 100) * this.state.duration);
}
audioContext.play()
} else {
audioContext.pause()
this.setState({
isAnimation: false
});
}
}
// 音频进度条拖放
hanleSliderChange(e) {
const position = e.detail.value
const currentTime = (position / 100) * this.state.duration
audioContext.seek(currentTime)
audioContext.play()
this.setState({
progressPrcent: position,
audioCurrent: currentTime
})
}
// 切换音频景点
toTabScenicInfo(info, index) {
// 这里切换音频时注意不需要再执行暂停事件了,因为重新初始化音频地址和name时会自动暂停上一个音频
this.setState({
isAnimation: false,
progressPrcent: 0,
audioCurrent: 0,
scenicInfo: info
}, () => {
this.initialization(info && info.voiceFile, info && info.name)
})
}
// 音频时间格式化
format(t) {
let time =
Math.floor(t / 60) >= 10 ? Math.floor(t / 60) : "0" + Math.floor(t / 60);
t = time + ":" + ((t % 60) / 100).toFixed(2).slice(-2);
return t;
}
render() {
const recordList = this.props.subScenic.list
const { isAnimation, duration, audioCurrent, progressPrcent, scenicInfo } = this.state;
const list = ((recordList && recordList.records) || []).map((item, index) => {
return (
<View className="list-item-wrap" key={String(index)}>
<View
className={listActive === item.subScenicId ? 'list-item active' : 'list-item'}
onClick={this.toTabScenicInfo.bind(this, item, index)}
>
{
index > 2 && <View className="vips">
<View className="ribbon">VIP</View>
</View>
}
{item.name}
</View>
</View>
);
});
return (
<View className="audios">
<View className="img-audio">
{!isAnimation ? (
<Image
className="audio_btn"
src={icon_stop}
onClick={this.playAudio.bind(
this, scenicInfo.voiceFile, scenicInfo.name
)}
/>
) : (
<Image
className="audio_btn"
src={icon_play}
onClick={this.playAudio.bind(
this, scenicInfo.voiceFile, scenicInfo.name
)}
/>
)}
<View>语音解说</View>
</View>
<View className="progerss-con">
<Text className="left_text">{this.format(audioCurrent)}</Text>
<Slider
class="mp-slider-bar"
block-size="16"
value={progressPrcent}
activeColor="#e3c703"
className="progress-bar"
disabled={!scenicInfo.voiceFile}
onChange={this.hanleSliderChange.bind(this)}
/>
<Text className="right_text">{this.format(duration)}</Text>
</View>
</View>
);
}
}