需求分析
页面中包含循环播放按钮、播放暂停按钮、倍数播放按钮、音频播放进度条、音频时长。由于个性化的设计,就无法使用audio组件实现。还好uniapp提供了API的方式来播放音频 ,下文会讲到API的一些重要信息,详情可以查看API文档。接下来分析具体实现步骤:
- 绘制UI页面
- 通过条件判断循环播放状态
- 通过条件判断播放或暂停状态
- 实现倍速播放
- 获取音频时长和当前播放时长并和进度条绑定
- 进度条使用slider组件,通过
change
事件控制音频的进度
API重要信息
this.audioContext = uni.createInnerAudioContext() //创建audio实例
src:播放地址 play:播放 pause:暂停 seek:跳转到音频指定位置
duration:音频播放长度 currentTime:当前音频播放位置
destroy:销毁当前实例 playbackRate:音频播放倍率
onTimeUpdate:音频播放进度更新事件 onCanplay:音频进入可播放状态事件
onEnded:音频自然播放结束事件 onError:音频播放错误事件
具体实现
第一步使用uniapp绘制ui页面(css略),定义一些变量
<view class="main-top">
<!-- video标签 -->
<view class="flex-between icon-box">
<image v-if="!isLoop" class="reset" src="/static/images/wiki/loop.svg" mode="" @click="isLoop = !isLoop"></image>
<image v-else class="reset" src="/static/images/wiki/loop-on.png" mode="" @click="isLoop = !isLoop"></image>
<image
v-if="!isPlaying"
class="play"
src="/static/images/wiki/play.svg"
mode=""
@click="handleChangeAudioState"
></image>
<image v-else class="play" src="/static/images/wiki/pause.png" mode="" @click="handleChangeAudioState"></image>
<view class="multiple">
<view @click="multipleShow = !multipleShow">倍数</view>
<view class="multiple-list" v-show="multipleShow">
<view class="" v-for="item in multiple" :key="item" @click="handleChageSpeed(item)">{{ item }}X</view>
</view>
</view>
</view>
<view class="mt10 pl30 pr30 flex-between">
<slider
style="width: 560rpx"
@change="handleSliderChange"
:value="sliderIndex"
:max="maxSliderIndex"
activeColor="#2377ff"
backgroundColor="dddddd"
block-color="#2377ff"
block-size="16"
/>
<view class="ml10 font28">{{ currentTimeText }}/{{ totalTimeText }}</view>
</view>
</view>
data() {
return {
isPlaying: false, //播放状态
multipleShow: false, //是否展示播放倍速
sliderIndex: 0, //滑块当前值
maxSliderIndex: 100, //滑块最大值
isLoop: false, //是否循环播放
playSpeed: "1.0", //当前播放速度
multiple: ["0.5", "1.0", "1.5"], //播放速度列表
audioContext: null, //音频实例
totalTimeText: "00:00", //视频总长度文字
currentTimeText: "00:00", //视频已播放长度文字
};
},
接着在组件的created
生命周期中创建audio
实例,设置实例的src
,并监听实例的onCanplay(音频进入可播放状态事件)
、onTimeUpdate(音频播放进度更新事件)
、onEnded(音频播放结束事件)
、onError(音频播放错误事件)
,考虑到代码的可重用,对逻辑进行函数封装。
created() {
// 获取 video 对象
let that = this;
console.info(this.src);
this.audioContext = uni.createInnerAudioContext();
this.audioContext.src = this.src;
let audio = this.audioContext;
audio.onCanplay(function () {
that.totalTimeText = that.getFormateTime(audio.duration);
that.maxSliderIndex = parseFloat(audio.duration).toFixed(2);
});
audio.onTimeUpdate(function () {
that.onTimeUpdate(audio);
});
audio.onEnded(function () {
that.audioEnd();
});
audio.onError(function () {
that.onError();
});
},
//时间转换函数,转换为00:00:00
getFormattedTime(time) {
const ms = time * 1000;
const date = new Date(ms);
const hour = date.getUTCHours();
const minute = date.getMinutes();
const second = date.getSeconds();
const formatTime = hour > 0
? `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}:${second.toString().padStart(2, '0')}`
: `${minute.toString().padStart(2, '0')}:${second.toString().padStart(2, '0')}`;
return formatTime;
}
//音频播放事件处理函数
onTimeUpdate(audio) {
//将当前音频长度秒转换为00:00:00格式
this.currentTimeText = this.getFormateTime(audio.currentTime);
this.sliderIndex = parseFloat(audio.currentTime).toFixed(2);
},
//音频播放结束处理函数
audioEnd() {
//通过延迟防止页面卡顿
let timer = setTimeout(() => {
this.sliderIndex = 0;
//播放结束后,进度回到0
this.changePlayProgress(0);
//如果开启循环播放,且结束前还是播放状态
if (this.isLoop && this.isPlaying) {
this.audioPlay();
} else {
this.audioPause();
}
timer = null;
}, 1000);
},
//音频播放错误处理函数,根据错误提示完善
errorFn(err) {
console.log(err);
}
实例化完成后,处理音频暂停、播放事件以及进度拖拽事件
//开始播放
audioPlay() {
this.audioContext.play();
this.isPlaying = true;
},
//暂停播放
audioPause() {
this.audioContext.pause();
this.isPlaying = false;
},
//变更滑块位置
handleSliderChange(e) {
this.changePlayProgress(e.detail ? e.detail.value : e);
},
//更改播放进度
changePlayProgress(value) {
this.sliderIndex = value;
this.audioContext.seek(value);
this.currentTimeText = this.getFormateTime(value);
},
最后处理音频倍速播放,以及销毁实例,考虑onHide
事件中是否暂停音频播放
//更改播放倍速,先暂停在播放,防止页面卡顿
handleChageSpeed(item) {
//暂停播放
this.audioPause();
//变更播放倍速,进度参数只能为数字
this.audioContext.playbackRate = Number(item);
//开始播放
this.audioPlay();
},
//销毁audioContext()实例
audioDestroy() {
if (this.audioContext) {
this.audioPause();
this.audioContext = null;
this.isPlaying = false;
}
},
//在组件销毁前销毁实例
beforeDestroy() {
this.audioDestroy();
},
//页面隐藏时暂停播放
onHide() {
this.audioPause();
},
总结
- 使用
uni.createInnerAudioContext
创建audio
实例 - 在
created
生命周期中监听audio
的事件,并封装函数处理逻辑 - 考虑页面加载是否流畅,组件销毁和页面隐藏