这篇文章主要记录一下小程序音频播放的整体流程。主要用到uiapp组件库的api uni.createInnerAudioContext()https://uniapp.dcloud.net.cn/api/media/audio-context.html#createinneraudiocontext
完成的功能有1.进入页面直接播放当前音频 2.进度条拖动音频快进快退 3.切换上一首下一首 4.当前音频播放完毕自动切换下一首
先看一下完成后的样式
样式就是这样了,基本满足我的项目需求。
直接放代码,复制完需要把接口请求完的数据更换下就可以使用了。样式还需要根据自己项目需求进行更改。
<template>
<view style="height: 100vh">
<view class="" v-if="videoMe.contents" style="height: 45vh;">
<view class="audios">
<view class="" style="margin-top: 20rpx;">
<image class="imgs" :src="userinfo.list_img" mode="widthFix"></image>
</view>
<view class="title">
{{videoMe.title}}
</view>
<view class="jinds" v-if="sliderMax">
<slider class="slider" min="0" :max="sliderMax" @change="sliderChangeComplate" block-size="14"
:value="sliderValue" activeColor="#0060FF"></slider>
<view class="time_flex">
<view class="">
{{currentTimeStr}}
</view>
<view class="">
{{alltime}}
</view>
</view>
</view>
<view class="img_flex">
<image @click="leftplay()" class="imgs1" src="/static/images/audio/left.png" mode=""></image>
<image @click="changePlayState()" v-show="!audioPlay" class="imgs2" src="/static/images/audio/stop.png" mode=""></image>
<image @click="changePlayState()" v-show="audioPlay" class="imgs2" src="/static/images/audio/open.png" mode=""></image>
<image @click="rightplay()" class="imgs1" src="/static/images/audio/right.png" mode=""></image>
</view>
<view class="video-title-tips">
<text style="display: block; " class="u-m-b-10">
共:{{userinfo.num}}期(已完结)<text style="padding-left: 20rpx;">{{userinfo.number}}人学过</text>
</text>
</view>
<view class="" style="height: 12rpx;background-color: #eee;"></view>
</view>
</view>
<!-- 视频推荐-->
<view class="textss" style="height: 45vh">
<view class="titles_t">
<view class="x"></view>
<view class="tit">课程目录</view>
</view>
<view class="videolist">
<scroll-view scroll-y="true" style="height: 40vh" >
<view class="videolist_item" v-for="(item,index) in videoArr" :key="index" @click="filechange(item,index)">
<view class="video_header">
<image v-if="videoMe.clid == item.clid" src="/static/images/deopen.png" mode=""></image>
<image v-else src="/static/images/destop.png" mode=""></image>
<view class="tit" :style="{color:videoMe.clid == item.clid?'#0060FF':'#000' }">
{{item.title}}
</view>
</view>
<view class="video_content">
<view class="video_content_flex">
<view class="titles">音频</view>
<view class="times">
{{item.times ?item.times:'暂无'}}
</view>
</view>
<view class="typetext">
学习中
</view>
</view>
</view>
<uni-load-more v-show="isok" :status="status"></uni-load-more>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
import {
getCurriculum
} from '@/common/http.api.js';
export default {
data() {
return {
videoMe: {},
videoArr: [],
userinfo: {},
params: {
clid: '',
},
isok: true,
status: 'loading',
indexplay:0,//key值 也是左右切换要用到的
alltime:'', //总时间
recordPath:'', //播放路径
currentTimeStr:'',//时间变换
audioPlay:true, //按钮切换
sliderValue:'',
sliderMax:'' //拖动条最大值
}
},
onLoad(query) {
this.params.clid = query.clid
this.getVideoFun()
},
//页面卸载 销毁实例
onUnload() {
this.innerAudioContext.destroy()
},
//页面隐藏 销毁实例
onHide: function() {
this.innerAudioContext.destroy()
},
methods: {
//请求接口拿到上来就要播放的 音频url
getVideoFun() {
getCurriculum(this.params).then(res => {
if (res.code == 200) {
this.userinfo = res.lecturer
this.videoMe = res.info
//音频url
this.recordPath = res.info.contents
//url拿到后开始执行音频实列
this.creatAudio()
this.videoArr = res.list
this.videoArr.forEach((item,index)=>{
if(this.videoMe.clid == item.clid){
this.indexplay = index
}
})
this.isok = false
this.status = 'no-more'
}
})
},
//音频实例
creatAudio() {
this.innerAudioContext = uni.createInnerAudioContext();//创建实例
this.innerAudioContext.autoplay = true;//设置是否自动播放
this.innerAudioContext.src = this.recordPath;//音频的url
this.innerAudioContext.onPlay(() => {
// 播放监听
console.log('播放!');
this.audioPlay = true;
});
this.innerAudioContext.onPause(() => {
// 暂停监听
console.log('暂停播放!');
this.audioPlay = false
});
this.innerAudioContext.onEnded(() => {
// 结束播放监听
console.log('播放结束!');
this.audioPlay=false;
//自动切换事件
this.qeihuanwenjian()
});
//音频播放进度更新事件
this.innerAudioContext.onTimeUpdate(() => {
const { currentTime,duration} = this.innerAudioContext;//这俩参数是这个api自带的参数
//转换开始时间 以及后面音频时间自增都是这个
const currTimeStr = this.formatTime(currentTime);
//这个是音频的总时长
const allt = this.formatTime(duration);
this.sliderValue = parseInt(currentTime);
this.alltime = allt
// 变动的时间
this.currentTimeStr = currTimeStr;
//进度条最大值
this.sliderMax = duration;
});
},
//格式化时间格式
formatTime(num) {
num = num.toFixed(0);
let second = num % 60;
if (second < 10) second = '0' + second;
let min = Math.floor(num / 60);
if (min < 10) min = '0' + min;
return min + ":" + second;
},
//按钮播放暂停
changePlayState() {
if (this.audioPlay == false) {
this.innerAudioContext.play();
} else {
this.innerAudioContext.pause()
}
},
//音频前进回退
sliderChangeComplate(e) {
const currTimeStr = this.formatTime(e.detail.value)
this.currentTimeStr = currTimeStr
this.innerAudioContext.seek(e.detail.value);
this.innerAudioContext.play();
},
// 文件切换播放
filechange(item, i) {
this.innerAudioContext.destroy()
this.videoMe = this.videoArr[i]
this.indexplay=i;
this.recordPath = item.contents;
this.creatAudio()
},
// 自动播放下一个文件
qeihuanwenjian(){
let index=this.indexplay+1;
if(index<this.videoArr.length){
this.indexplay=index;
let item=this.videoArr[this.indexplay];
let i=this.indexplay;
this.filechange(item,i)
}
},
//上一首
leftplay(){
if(this.indexplay == 0){
return;
}
this.indexplay --
this.innerAudioContext.destroy()
this.videoMe = this.videoArr[this.indexplay]
this.recordPath = this.videoArr[this.indexplay].contents;
this.creatAudio()
},
//下一首
rightplay(){
if(this.indexplay == (this.videoArr.length - 1)){
return;
}
this.indexplay ++
this.innerAudioContext.destroy()
this.videoMe = this.videoArr[this.indexplay]
this.recordPath = this.videoArr[this.indexplay].contents;
this.creatAudio()
},
}
}
</script>
<style>
page {}
</style>
<style lang="scss" scoped>
.audios{
.imgs{
width: 460rpx;
height: 280rpx;
box-shadow: 0rpx 10rpx 10rpx 2rpx rgba(101,101,101,0.07);
border-radius: 2rpx 2rpx 2rpx 2rpx;
margin: 0 auto;
display: block;
}
.title{
text-align: center;
font-size: 32rpx;
font-family: PingFang SC-Heavy, PingFang SC;
font-weight: bold;
color: #000000;
margin: 20rpx;
}
.img_flex{
display: flex;
align-items: center;
width: 400rpx;
margin: 10rpx auto;
justify-content: space-between;
.imgs1{
width: 34rpx;
height: 38rpx;
}
.imgs2{
width: 110rpx;
height: 110rpx;
}
}
.video-title-tips {
color: #666;
font-size: 28rpx;
font-weight: 500;
text-align: center;
margin-bottom: 20rpx;
}
}
.jinds{
width: 500rpx;
margin: 0 auto;
.slider{
margin: 0rpx;
}
.time_flex{
display: flex;
justify-content: space-between;
font-size: 24rpx;
color: #999999;
margin: 4rpx 0rpx;
}
}
.btn {
border-radius: 24rpx;
margin-top: 40rpx;
}
.img-txt {
padding: 20rpx;
}
.userinfos {
display: flex;
padding: 30rpx 24rpx;
box-sizing: border-box;
.userinfosimg {
image {
width: 104rpx;
height: 104rpx;
border-radius: 52rpx;
}
}
.userinfostext {
margin-left: 20rpx;
.usertitle {
font-size: 30rpx;
font-weight: 400;
color: #333333;
font-weight: bold;
}
.usertitles {
line-height: 80rpx;
color: #666;
font-size: 28rpx;
font-weight: 500;
}
}
}
.textss {
padding: 30rpx 24rpx;
box-sizing: border-box;
.titles_t {
display: flex;
align-items: center;
.x {
width: 10rpx;
height: 44rpx;
background-color: #0060FF;
border-radius: 29rpx 29rpx 29rpx 29rpx;
}
.tit {
margin-left: 20rpx;
font-size: 34rpx;
font-family: PingFang SC-Heavy, PingFang SC;
font-weight: bold;
color: #000000;
}
}
.videolist {
.videolist_item {
border-bottom: 1px solid #EAEAEA;
margin: 30rpx auto;
}
.video_header {
display: flex;
align-items: center;
image {
width: 38rpx;
height: 38rpx;
}
.tit {
margin-left: 20rpx;
font-size: 34rpx;
font-family: PingFang SC-Heavy, PingFang SC;
font-weight: bold;
color: #000000;
}
}
.video_content {
display: flex;
align-items: center;
justify-content: space-between;
margin: 30rpx auto;
padding: 0rpx 0rpx 0rpx 50rpx;
.video_content_flex {
display: flex;
align-items: center;
.titles {
border: 1px solid #999999;
border-radius: 6rpx;
font-size: 26rpx;
color: #999999;
padding: 2rpx 10rpx;
}
.times {
font-size: 26rpx;
color: #999999;
margin-left: 20rpx;
}
}
.typetext {
font-size: 26rpx;
color: #999999;
}
}
}
}
</style>