uniapp uses uni.createInnerAudioContext() to implement h5 audiobook playback in the app applet

Realize the listening function of reading software, adapt to apps, small programs, and h5

The effect display function brings you: Chapter, countdown, previous chapter, next chapter, play, pause, double speed:
uniapp official uni.createInnerAudioContext() document address: official website document reference address
First introduce the function step by step:

  1. Chapter (this call interface can traverse the data and display it in the form of a pop-up layer)
  2. Countdown
  3. Previous chapter
  4. next chapter
  5. play
  6. pause
  7. double speed

I embed an audio component on this page, and the component name is 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 page code:

<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>

First enter the page initialization method:

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()
	});
},

If you need to pause the audio when the page returns:

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

Click the progress bar to jump to the specified location:

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

Play function

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

For the previous chapter and the next chapter, our chapter directory structure is two-level. The logic is to find the corresponding chapter in the chapter array based on the chapter id that is clicked, and then take its previous node or next node. Note: Replace the obtained id with the current 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()
	});
},

Guess you like

Origin blog.csdn.net/weixin_43877575/article/details/131291402