微信小程序-写一个音频播放器

这篇文章主要记录一下小程序音频播放的整体流程。主要用到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>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值