uniapp使用uni.createInnerAudioContext实现音频的循环播放、进度拖拽、倍速播放

发布于:2024-05-08 ⋅ 阅读:(18) ⋅ 点赞:(0)

image-20240506133916150.png

image-20240506133937628.png

需求分析

页面中包含循环播放按钮、播放暂停按钮、倍数播放按钮、音频播放进度条、音频时长。由于个性化的设计,就无法使用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();
},

总结

  1. 使用uni.createInnerAudioContext创建audio实例
  2. created生命周期中监听audio的事件,并封装函数处理逻辑
  3. 考虑页面加载是否流畅,组件销毁和页面隐藏

网站公告

今日签到

点亮在社区的每一天
去签到