实现读书软件的听书功能,适配app,小程序,h5
实现效果展示功能带你包括: 章节,倒计时,上一章,下一章,播放,暂停,倍速:
uniapp官方uni.createInnerAudioContext()的文档地址:官网文档参考地址
首先分步骤介绍功能:
- 章节(这个调取接口遍历数据就可以,弹出层的形式展示)
- 倒计时
- 上一章
- 下一章
- 播放
- 暂停
- 倍速
我这里页面嵌入一个audio的组件 ,组件名称audio
components: {Audio},data() {return {sourceList:[], //章节的数据bookImg: '', //图书的封面图bookName:'',//书名chapterName: '', //章节名称chapterUrl:'',//播放的章节地址id: null, //书的iditemId:'', //章节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 = thisthat.initData()},initData() {let that = this// 页面加载设置当前播放章节的播放信息this.bgAudioMannager = uni.createInnerAudioContext(); //只创建一次之后都是通过this.bgAudioMannager获取this.bgAudioMannager.coverImgUrl = this.bookImgthis.bgAudioMannager.title = this.chapterNamethis.bgAudioMannager.src = this.chapterUrl// this.bgAudioMannager.playbackRate = 1.0 默认倍速为1.0this.newId = this.idthis.duration = this.bgAudioMannager.durationthis.currentTime = this.bgAudioMannager.currentTimethis.currentId = this.idthis.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.currentTimethis.duration = this.bgAudioMannager.durationconsole.log("可播放状态")if (this.bgAudioMannager.duration) {this.duration = this.bgAudioMannager.durationconsole.log(this.bgAudioMannager.duration)}})this.bgAudioMannager.play()//音频进度更新事件this.bgAudioMannager.onTimeUpdate(() => {// console.log("开始监听")if (!this.seek) {this.currentTime = this.bgAudioMannager.currentTime} else {console.log("修改一次进度条")// this.audio.seek(this.current_tmp)// this.current = this.current_tmpconsole.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 = valthis.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 = thisvar currentIndex = nullvar lastIndex = nullvar currentIdx = nullthis.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].chapterNamethis.bgAudioMannager.coverImgUrl = item.twoLevelChapter[currentIdx - 1].chapterUrlthis.bgAudioMannager.src = item.twoLevelChapter[currentIdx - 1].chapterUrlthat.newId = item.twoLevelChapter[currentIdx - 1].idthat.$emit('handleId', this.newId)this.nextPlay = truethis.playStatus = truethis.bgAudioMannager.play()} else {//如果刚好拿到的是第一章节就直接给提示if (index == 0) {this.bgAudioMannager.title = item.twoLevelChapter[0].chapterNamethis.bgAudioMannager.coverImgUrl = item.twoLevelChapter[0].chapterUrlthis.bgAudioMannager.src = item.twoLevelChapter[0].chapterUrlthat.newId = item.twoLevelChapter[0].iduni.showToast({title: '没有上一章节了',icon: 'none'})return} else {this.bgAudioMannager.title = that.sourceList[index - 1].chapterNamethis.bgAudioMannager.coverImgUrl = that.sourceList[index - 1].coverImgUrlthis.bgAudioMannager.src = that.sourceList[index - 1].chapterUrlthat.newId = that.sourceList[index - 1].idthat.$emit('handleId', this.newId)}}}})} else {lastIndex = this.sourceList.length - 1if (currentIndex != 0) {this.bgAudioMannager.title = that.sourceList[currentIndex - 1].chapterNamethis.bookTitle = this.bgAudioMannager.titleconsole.log('title', this.bgAudioMannager.title)this.bgAudioMannager.coverImgUrl = this.sourceList[currentIndex - 1].coverImgUrlthis.bgAudioMannager.src = this.sourceList[currentIndex - 1].chapterUrlthis.newId = this.sourceList[currentIndex - 1].idthat.$emit('handleId', this.newId)this.nextPlay = truethis.playStatus = truethis.bgAudioMannager.play()} else {this.bgAudioMannager.title = that.sourceList[0].chapterNamethis.bgAudioMannager.src = this.sourceList[0].chapterUrlthis.newId = this.sourceList[0].idthis.bgAudioMannager.play()uni.showToast({title: '已经是第一章了',icon: 'none'})return}}this.id = this.newId},// 下一首nextMusic() {let that = thisvar currentIndex = nullvar lastIndex = nullvar currentIdx = nullthis.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 - 1if (currentIdx != -1) {if (currentIdx != lastIndex) {this.bgAudioMannager.title = item.twoLevelChapter[currentIdx + 1].chapterNamethis.bgAudioMannager.coverImgUrl = item.twoLevelChapter[currentIdx + 1].chapterUrlthis.bgAudioMannager.src = item.twoLevelChapter[currentIdx + 1].chapterUrlthis.id = item.twoLevelChapter[currentIdx + 1].idthis.nextPlay = truethis.playStatus = truethis.bgAudioMannager.play()} else {this.bgAudioMannager.title = that.sourceList[currentIndex + 1].chapterNamethis.bgAudioMannager.coverImgUrl = that.sourceList[currentIndex + 1].chapterUrlthis.bgAudioMannager.src = that.sourceList[currentIndex + 1].chapterUrlthat.id = that.sourceList[currentIndex + 1].idthis.bgAudioMannager.play()}}})} else {lastIndex = this.sourceList.length - 1if (currentIndex != lastIndex) {this.bgAudioMannager.title = this.sourceList[currentIndex + 1].chapterNamethis.bgAudioMannager.coverImgUrl = this.sourceList[currentIndex + 1].chapterUrlthis.bgAudioMannager.src = this.sourceList[currentIndex + 1].chapterUrlthis.id = this.sourceList[currentIndex + 1].idthis.nextPlay = truethis.playStatus = truethis.bgAudioMannager.play()} else {this.bgAudioMannager.title = this.sourceList[lastIndex].chapterNamethis.bgAudioMannager.coverImgUrl = this.sourceList[lastIndex].chapterUrlthis.bgAudioMannager.src = this.sourceList[lastIndex].chapterUrlthis.id = this.sourceList[lastIndex].idthis.bgAudioMannager.play()uni.showToast({title: '已经是最后一章了',icon: 'none'})return}}//如果播放自动播放完就自动切换下一章this.bgAudioMannager.onEnded(() => {//初始化 需要的参数console.log('自然播放结束eee事件');this.nextMusic()});},
来源地址:https://blog.csdn.net/weixin_43877575/article/details/131291402