uniapp使用uni.createInnerAudioContext()实现在app 小程序 h5有声书的播放

实现读书软件的听书功能,适配app,小程序,h5

实现效果展示功能带你包括: 章节,倒计时,上一章,下一章,播放,暂停,倍速:
uniapp官方uni.createInnerAudioContext()的文档地址:官网文档参考地址
首先分步骤介绍功能:

  1. 章节(这个调取接口遍历数据就可以,弹出层的形式展示)
  2. 倒计时
  3. 上一章
  4. 下一章
  5. 播放
  6. 暂停
  7. 倍速

我这里页面嵌入一个audio的组件 ,组件名称audio

components: {
    
    Audio},
data() {
    
    
	return {
    
    
		sourceList:[], //章节的数据
		bookImg: '', //图书的封面图
		bookName:'',//书名
		chapterName: '', //章节名称
		chapterUrl:'',//播放的章节地址
		id: null, //书的id
		itemId:'', //章节id
	}
},
<template>
	<view class="content">
		<view class="" v-if="sourceList.length > 0">
			<Audio @handleId='handleId'  :sourceList='sourceList' :bookResourceId='bookResourceId' :bookName='bookName' :bookImg='bookImg' :id='id' :wxId='id' :chapterName='chapterName' :chapterUrl='chapterUrl'></Audio>
		</view>
	</view>
</template>
handleId(id){
    
    
	this.id = id
},

Audio的页面代码:

<template>
	<view class="audio_content">
		<view class="audio_box">
			<image class="audio-img" :src="bookImg" mode="aspectFill"></image>
			<view class="img-pop">

			</view>
			<view class="audio-info">
				<!-- #ifdef APP-PLUS -->
				<view class="header-box">
					<u-icon name="arrow-left" style="margin-left: 32rpx;" color="#ffffff" size="24"
						@click="goBack()"></u-icon>
					<u-icon style="margin-right: 44rpx;" name="share-square" color="#ffffff" size="24"
						@click="appShare"></u-icon>

				</view>
				<!-- #endif -->
				<view class="course_name">
					{
    
    {
    
    bookName}}
				</view>
				<view class="audio_title">
					{
    
    {
    
    bgAudioMannager.title}}
					<!-- {
    
    {
    
    bookTitle}} -->
				</view>
				<view class="book-box">
					<image class="book-img" :src="bookImg" mode="aspectFill"></image>
				</view>
				<view class="audio_process">
					<view class='slider'>
						<!-- <slider :value="currentTime" step="1" min="0" :max="duration"
							block-color="#fff" activeColor="#DC3232" inactiveColor="#8B8D7F" block-size="12"
							@change="seek=true,clickSeek($event.detail.value)" @changing="seek=true,current=$event.detail.value" /> -->
						<slider class="audio-slider" activeColor="#DC3232" block-size="12" :value="currentTime"
							:max="duration" @changing="seek=true,current=$event.detail.value"
							@change="seek=true,clickSeek($event.detail.value)"></slider>
					</view>
					<view class="time_cons">
						<view class="duration">
							{
    
    {
    
     time.getAudioTime(currentTime) }}
						</view>
						<view class="end">
							{
    
    {
    
     time.getAudioTime(duration) }}
						</view>
					</view>
				</view>
				<view class="utils">
					<view @tap="showModal">
						<image class="change-list" :src="$staticUrl + 'static/index/audio-list.png'" mode="">
						</image>
					</view>
					<view class="timeBox" @click="openTime">
						<image class="change-time" :src="$staticUrl + 'static/index/audio-time.png'" mode="">
						</image>
						<view class="timeText">
							{
    
    {
    
    timeMsg}}
						</view>
					</view>
					<!-- 上一章 -->
					<view class="">
						<image v-if="!lastPlay" @click="lastMusic" class="change"
							:src="$staticUrl + 'static/audio.png'" />
						<image @click="lastMusic" class="change" v-if="lastPlay"
							:src="$staticUrl + 'static/index/audio-pre.png'" />
					</view>
					<!-- 播放 -->
					<view class="">
						<image @click="playOrpause" v-if="!playStatus" class="change-start"
							:src="$staticUrl + 'static/index/audio-start.png'" />
						<image @tap="playOrpause" v-if="playStatus" class="change-start"
							:src="$staticUrl + 'static/index/jgq_sxzt.png'" />
					</view>
					<!-- 下一章 -->
					<view class="prev next">
						<image @click="nextMusic" v-if="nextPlay" class="change"
							:src="$staticUrl + 'static/index/audio-next.png'" />
						<image v-if="!nextPlay" class="change" :src="$staticUrl + 'static/audio-g.png'" />
					</view>

					<view class="beisu" @click="showPlaybackRate = true">
						<text class="num">{
    
    {
    
    rateText}}</text>
						<!-- <text class="text">倍速播放</text> -->
					</view>
				</view>

			</view>

		</view>
		<u-popup :show="hideModal" @close="close" @open="open" :round="10">
			<!-- <view class="empty-box" id="empty-box"></view> -->
			<scroll-view scroll-y style="height: 100%;">
				<view class="content" :style="'transform:translateY(' + translateY + 'px);'" :animation="animate">
					<view class="header">
						<view class="title">
							目录
						</view>
						<view class="right" @click="close">
							<u-icon name="close" color="#000000" size="20"></u-icon>
						</view>
					</view>

					<view>
						<PeriodList :bookCoverImg='bookImg' :bookName='bookName' :supCode='supCode'
							:sourceType='sourceType' :id='id' type='audio' :chapterList='sourceList'
							@startAudio='startAudio' :bookResourceId='bookResourceId'></PeriodList>
					</view>
				</view>

			</scroll-view>
		</u-popup>
		<!-- 倍速弹出层 -->
		<u-popup :show="showPlaybackRate" @close="closePlaybackRate" @open="openPlaybackRate" :round="10">
			<!-- <view class="empty-box" id="empty-box"></view> -->
			<scroll-view scroll-y style="height: 100%;">
				<view class="content" :style="'transform:translateY(' + translateY + 'px);'" :animation="animate">
					<view class="header">
						<view class="title">
							倍速选择
						</view>
						<view class="right" @click="closePlaybackRate">
							<u-icon name="close" color="#000000" size="20"></u-icon>
						</view>
					</view>

					<view class="rate-listBox">
						<view class="rate-list" @click="handleRate(0.5)">
							0.5倍速
						</view>
						<view class="rate-list" @click="handleRate(1)">
							1.0倍速
						</view>
						<view class="rate-list" @click="handleRate(1.5)">
							1.5倍速
						</view>
						<view class="rate-list" @click="handleRate(2.0)">
							2.0倍速
						</view>
					</view>
				</view>

			</scroll-view>
		</u-popup>
		<u-popup :show="showCloseTime" @close="closeTime" @open="openTime" :round="10">
			<!-- <view class="empty-box" id="empty-box"></view> -->
			<scroll-view scroll-y style="height: 100%;">
				<view class="content" :style="'transform:translateY(' + translateY + 'px);'" :animation="animate">
					<view class="header">
						<view class="title">
							设置倒计时关闭
						</view>
						<view class="right" @click="closeTime">
							<u-icon name="close" color="#000000" size="20"></u-icon>
						</view>
					</view>

					<view class="rate-listBox">
						<!-- <view class="rate-list" @click="handleCloseTime(11)">
							1分钟
						</view>
						<view class="rate-list" @click="handleCloseTime(12)">
							5分钟
						</view> -->
						<view class="rate-list" @click="handleCloseTime(15)">
							15分钟
						</view>
						<view class="rate-list" @click="handleCloseTime(30)">
							30分钟
						</view>
						<view class="rate-list" @click="handleCloseTime(45)">
							45分钟
						</view>
						<view class="rate-list" @click="handleCloseTime(1)">
							1小时
						</view>
						<view class="rate-list" @click="handleCloseTime(2)">
							2小时
						</view>
					</view>
				</view>

			</scroll-view>
		</u-popup>
		<!-- #ifdef APP-PLUS -->
		<sharePop :shows="shows" :href="href" :title='title' @closes='closes'></sharePop>
		<!-- #endif -->
		
	</view>
</template>

首先进入页面初始化方法:

mounted() {
    
    
	let that = this
		that.initData()
	},
initData() {
    
    
	let that = this
	// 页面加载设置当前播放章节的播放信息
	this.bgAudioMannager = uni.createInnerAudioContext(); //只创建一次之后都是通过this.bgAudioMannager获取
	this.bgAudioMannager.coverImgUrl = this.bookImg
	this.bgAudioMannager.title = this.chapterName
	this.bgAudioMannager.src = this.chapterUrl
	// this.bgAudioMannager.playbackRate = 1.0 默认倍速为1.0
	this.newId = this.id
	this.duration = this.bgAudioMannager.duration
	this.currentTime = this.bgAudioMannager.currentTime
	this.currentId = this.id
	this.startTime = this.getDate()
	this.bgAudioMannager.onPlay(() => {
    
    
		console.log('开始播放');
	});
	this.bgAudioMannager.onStop(() => {
    
    
		console.log('停止播放');
	});

	this.bgAudioMannager.onPause(() => {
    
    
		console.log('暂停播放');
	});
	this.bgAudioMannager.onEnded(() => {
    
    
		//初始化 需要的参数
		console.log('自然播放结束事件');
		// this.nextMusic()
	});
	this.bgAudioMannager.onError((res) => {
    
    
	
		console.log(res.errMsg);
		console.log(res.errCode);
	});
	// 重要 缺失 音频进入可以播放状态
	this.bgAudioMannager.onCanplay(() => {
    
    
		this.currentTime = this.bgAudioMannager.currentTime
		this.duration = this.bgAudioMannager.duration
		console.log("可播放状态")
		if (this.bgAudioMannager.duration) {
    
    
			this.duration = this.bgAudioMannager.duration
			console.log(this.bgAudioMannager.duration)
		}
	})
	this.bgAudioMannager.play()
	//音频进度更新事件
	this.bgAudioMannager.onTimeUpdate(() => {
    
    
		// console.log("开始监听")
		/* 
		判断是否点击过进度条,若点击过,则不要对当前进度条时间current赋currentTime的值
		因为音频进度更新事件运行频率过快,两个时间会引起冲突,
		因此需要通过设置开关,判断seek真假,若seek为假则未点击进度条,若seek为真则跳过此次赋值并修改seek值重置为假
		*/
		if (!this.seek) {
    
    
			this.currentTime = this.bgAudioMannager.currentTime
		} else {
    
    
			console.log("修改一次进度条")
			// this.audio.seek(this.current_tmp)
			// this.current = this.current_tmp
			console.log(this.currentTime)
			this.seek = false
		}
		if (this.bgAudioMannager.duration) {
    
    
			this.duration = this.bgAudioMannager.duration
		}
	})
	this.bgAudioMannager.onEnded(() => {
    
    
		console.log('播放结束')
		this.nextMusic()
	});
},

如果需要页面返回的时候暂停音频:

onUnload() {
    
    
	// clearInterval(timeSet); //停止调用
	this.bgAudioMannager.pause()
	this.newTime = 0;
	this.playStatus = false //暂停播放的状态
	this.bgAudioMannager.src = '' //当设置了新的 src 时,会自动开始播放
},
destroyed() {
    
    
	this.bgAudioMannager.pause()
	this.endTime = this.getDate()
},

点击进度条跳转到指定位置:

// 点击进度条
clickSeek(val) {
    
    
	this.currentTime = val
	this.bgAudioMannager.seek(val)
},

播放功能

playOrpause() {
    
    
	//根据播放状态进行播放还是暂停
	if (this.playStatus) {
    
    
		this.bgAudioMannager.pause()
		this.playStatus = false
	} else {
    
    
		this.bgAudioMannager.play()
		this.playStatus = true
	}
},

上一章和下一章,我们的章节目录结构是两层,逻辑是根据点击的进来的章节id去章节数组中找对应的章节,然后取他的上一节点或者下一节点,注意点:要把拿到的id替换掉当前的id

//上一首
lastMusic() {
    
    
	let that = this
	var currentIndex = null
	var lastIndex = null
	var currentIdx = null
	this.bgAudioMannager.pause()
	currentIndex = this.sourceList.findIndex((profile) => profile.id == that.id)
	if (currentIndex == null || currentIndex == -1) {
    
    
		this.sourceList.forEach((item, index) => {
    
    
			//看当前id是否在子章节当中
			currentIdx = item.twoLevelChapter.findIndex((item1) => item1.id == this.id)
			lastIndex = item.twoLevelChapter.length - 1
			//如果在父章节中找到,直接拿当前章节上一章节,通过下标-1的信息
			if (currentIdx != -1) {
    
    
				if (currentIdx != 0) {
    
    
					this.bgAudioMannager.title = item.twoLevelChapter[currentIdx - 1].chapterName
					this.bgAudioMannager.coverImgUrl = item.twoLevelChapter[currentIdx - 1].chapterUrl
					this.bgAudioMannager.src = item.twoLevelChapter[currentIdx - 1].chapterUrl
					that.newId = item.twoLevelChapter[currentIdx - 1].id
					that.$emit('handleId', this.newId)
					this.nextPlay = true
					this.playStatus = true
					this.bgAudioMannager.play()
				} else {
    
    
				//如果刚好拿到的是第一章节就直接给提示
					if (index == 0) {
    
    
						this.bgAudioMannager.title = item.twoLevelChapter[0].chapterName
						this.bgAudioMannager.coverImgUrl = item.twoLevelChapter[0].chapterUrl
						this.bgAudioMannager.src = item.twoLevelChapter[0].chapterUrl
						that.newId = item.twoLevelChapter[0].id
						uni.showToast({
    
    
							title: '没有上一章节了',
							icon: 'none'
						})
						return
					} else {
    
    
						this.bgAudioMannager.title = that.sourceList[index - 1].chapterName
						this.bgAudioMannager.coverImgUrl = that.sourceList[index - 1].coverImgUrl
						this.bgAudioMannager.src = that.sourceList[index - 1].chapterUrl
						that.newId = that.sourceList[index - 1].id
						that.$emit('handleId', this.newId)
					}

				}

			}
		})

	} else {
    
    
		lastIndex = this.sourceList.length - 1
		if (currentIndex != 0) {
    
    
			this.bgAudioMannager.title = that.sourceList[currentIndex - 1].chapterName
			this.bookTitle = this.bgAudioMannager.title
			console.log('title', this.bgAudioMannager.title)
			this.bgAudioMannager.coverImgUrl = this.sourceList[currentIndex - 1].coverImgUrl
			this.bgAudioMannager.src = this.sourceList[currentIndex - 1].chapterUrl
			this.newId = this.sourceList[currentIndex - 1].id
			that.$emit('handleId', this.newId)
			this.nextPlay = true
			this.playStatus = true
			this.bgAudioMannager.play()
		} else {
    
    
			this.bgAudioMannager.title = that.sourceList[0].chapterName
			this.bgAudioMannager.src = this.sourceList[0].chapterUrl
			this.newId = this.sourceList[0].id
			this.bgAudioMannager.play()
			uni.showToast({
    
    
				title: '已经是第一章了',
				icon: 'none'
			})
			return
		}
	}
	this.id = this.newId
},
// 下一首
nextMusic() {
    
    
	let that = this
	var currentIndex = null
	var lastIndex = null
	var currentIdx = null
	this.bgAudioMannager.pause()
	currentIndex = this.sourceList.findIndex((profile) => profile.id == this.id)
	if (currentIndex == null || currentIndex == -1) {
    
    
		this.sourceList.forEach(item => {
    
    
			currentIdx = item.twoLevelChapter.findIndex((item1) => item1.id == this.id)
			lastIndex = item.twoLevelChapter.length - 1
			if (currentIdx != -1) {
    
    
				if (currentIdx != lastIndex) {
    
    
					this.bgAudioMannager.title = item.twoLevelChapter[currentIdx + 1].chapterName
					this.bgAudioMannager.coverImgUrl = item.twoLevelChapter[currentIdx + 1].chapterUrl
					this.bgAudioMannager.src = item.twoLevelChapter[currentIdx + 1].chapterUrl
					this.id = item.twoLevelChapter[currentIdx + 1].id
					this.nextPlay = true
					this.playStatus = true
					this.bgAudioMannager.play()
				} else {
    
    
					this.bgAudioMannager.title = that.sourceList[currentIndex + 1].chapterName
					this.bgAudioMannager.coverImgUrl = that.sourceList[currentIndex + 1].chapterUrl
					this.bgAudioMannager.src = that.sourceList[currentIndex + 1].chapterUrl
					that.id = that.sourceList[currentIndex + 1].id
					this.bgAudioMannager.play()
				}

			}
		})

	} else {
    
    
		lastIndex = this.sourceList.length - 1
		if (currentIndex != lastIndex) {
    
    
			this.bgAudioMannager.title = this.sourceList[currentIndex + 1].chapterName
			this.bgAudioMannager.coverImgUrl = this.sourceList[currentIndex + 1].chapterUrl
			this.bgAudioMannager.src = this.sourceList[currentIndex + 1].chapterUrl
			this.id = this.sourceList[currentIndex + 1].id
			this.nextPlay = true
			this.playStatus = true
			this.bgAudioMannager.play()
		} else {
    
    
			this.bgAudioMannager.title = this.sourceList[lastIndex].chapterName
			this.bgAudioMannager.coverImgUrl = this.sourceList[lastIndex].chapterUrl
			this.bgAudioMannager.src = this.sourceList[lastIndex].chapterUrl
			this.id = this.sourceList[lastIndex].id
			this.bgAudioMannager.play()
			uni.showToast({
    
    
				title: '已经是最后一章了',
				icon: 'none'
			})
			return
		}
	}
	//如果播放自动播放完就自动切换下一章
	this.bgAudioMannager.onEnded(() => {
    
    
		//初始化 需要的参数
		console.log('自然播放结束eee事件');
		this.nextMusic()
	});
},

猜你喜欢

转载自blog.csdn.net/weixin_43877575/article/details/131291402