在小程序中开发音效盒子功能,公开源码
网上看到别的小程序播放音效还需要钱,定眼一看,这不很简单吗?一个分类、分类下放音频的url,再播放出来就行,说干就干。最终成品如下:
点击音效的时候,会先出现加载中的loading效果,因为mp3文件是oss上的,需要加载才能播放,播放中时会聚焦是哪个在播放。
播放中效果:
我的技术栈如下:使用uniapp,前端框架为:uview,菜单和音效url均通过接口获取,也可以写死在小程序里面(除非你不想更改)。
我的小程序功能已上线,体验使用请在微信中搜索:《一方云知》,拉到最下面有一个云音盒的菜单,点进去就是:
下面贴上全部代码(247行),基本上稍微改改配置部分就能用了:
<template> <view> <view v-if="scrollList.length"> <div class="header "> <u-scroll-list :indicator="false"> <view class="scroll-list" style="flex-direction: row;"> <view class="scroll-list__goods-item" @click="click_class(index)" v-for="(item, index) in scrollList" :key="index" :class="[(index === 9) && 'scroll-list__goods-item--no-margin-right']"> <image class="scroll-list__goods-item__image" :src="item.thumb"></image> <text class="scroll-list__goods-item__text " :class="{'selected':class_index==index}">{{ item.title }}</text> </view> </view> </u-scroll-list> </div> <swiper :current="class_index" class="swiper" circular="false" :indicator-dots="false" :autoplay="false" @change="swiper_change"> <swiper-item v-for="(item, index) in scrollList" :key="index"> <z-paging :show-empty-view-reload="false" :show-refresher-when-reload="false" style="height: 100vh;" ref="paging" :fixed="false" :list.sync="item.child" :auto="false" :refresher-enabled="false"> <div class="bgm_list"> <div class="bgm_item" :class="{'selected':item2.is_play}" v-for="(item2, index2) in item.child" :key="index2" @click="play_mp3(index, index2)"> <div class="title"> {{item2.title}} </div> <div class="icon" v-if="item2.mp3_url"> <u-icon name="volume-fill" color="black" size="20" v-if="!item2.is_play && !item2.loading"></u-icon> <u-loading-icon size="20" v-if="item2.loading"></u-loading-icon> <image v-if="item2.is_play" class="scroll-list__goods-item__image" src="/subpages/tool/static/playing.gif" style="width: 40rpx;height: 40rpx;"> </image> </div> </div> </div> <u-gap height="120" bgColor="#ffffff"></u-gap> </z-paging> </swiper-item> </swiper> </view> <view v-else style="margin-top: 100rpx;"> <u-loading-icon></u-loading-icon> </view> </view> </template> <script> export default { data() { return { class_index: 0, // 选项分类 scrollList: [], innerAudioContext: null, // 全局mp3播放对象 prev_index: null, // 上一次播放的mp3一级菜单 prev_index2: null, // 上一次播放的mp3二级菜单 }; }, onLoad() { this.load_config() }, methods: { async load_config() { this.scrollList = [ { title: '台词', // 菜单标题 thumb: 'https://cdn.uviewui.com/uview/goods/1.jpg', // 菜单封面 child: [{ title: '你已经死了', // 音效标题 mp3_url: 'https://yifangyunzhi-anime.oss-cn-shanghai.aliyuncs.com/audio/yunyinhe/%E4%BD%A0%E5%B7%B2%E7%BB%8F%E6%AD%BB%E4%BA%86_%E8%80%B3%E8%81%86%E7%BD%91_%5B%E5%A3%B0%E9%9F%B3ID%EF%BC%9A37127%5D.mp3', // 音效url is_play: false, // 是否在播放中 can_play: false, // mp3是否在加载完成 可以播放了,因为有些mp3比较大,加载需要一些时间 }, { title: '略略略吐舌头', mp3_url: 'https://yifangyunzhi-anime.oss-cn-shanghai.aliyuncs.com/audio/yunyinhe/%E7%95%A5%E7%95%A5%E7%95%A5%E5%90%90%E8%88%8C%E5%A4%B4_%E8%80%B3%E8%81%86%E7%BD%91_%5B%E5%A3%B0%E9%9F%B3ID%EF%BC%9A36461%5D.wav' }, { title: '汤姆惨叫声', mp3_url: 'https://yifangyunzhi-anime.oss-cn-shanghai.aliyuncs.com/audio/yunyinhe/%E6%B1%A4%E5%A7%86%E7%8C%AB_%E8%80%B3%E8%81%86%E7%BD%91_%5B%E5%A3%B0%E9%9F%B3ID%EF%BC%9A37188%5D.mp3' }, ], }, // 基本数据结构参考 ] }, // 将上一次播放的mp3置为未播放 也就是停止播放状态 reset_prev_play_state() { if (this.prev_index != null && this.prev_index2 != null) { this.play_end(this.prev_index, this.prev_index2) } }, set_prev_index(index, index2) { this.prev_index = index this.prev_index2 = index2 }, play_mp3(index, index2) { const that = this const mp3_url = that.scrollList[index].child[index2].mp3_url if (!mp3_url) return // 多次会调用播放新的文件时,提前销毁实例,可避免-99错误 this.reset_prev_play_state() this.set_prev_index(index, index2) if (that.innerAudioContext) { try { that.innerAudioContext.pause(); that.innerAudioContext.destroy() that.innerAudioContext = null } catch (e) { // TODO handle the exception } } that.scrollList[index].child[index2].loading = true // 音频正在加载中 that.innerAudioContext = uni.createInnerAudioContext(); that.innerAudioContext.autoplay = false; that.innerAudioContext.src = mp3_url; that.innerAudioContext.onCanplay(() => { that.play_start(index, index2) // 音频进入可以播放状态,但不保证后面可以流畅播放 that.scrollList[index].child[index2].loading = false // 音频加载成功 that.innerAudioContext.play(); console.log('音频进入可以播放状态'); }); that.innerAudioContext.onPlay(() => { // 同一个mp3地址只触发一次回调 不知道怎么回事 console.log('加载完成 开始播放'); }); that.innerAudioContext.onError((res) => { console.log('播放失败', res.errMsg, res.errCode); that.play_end(index, index2) }); that.innerAudioContext.onEnded(() => { that.play_end(index, index2) // console.log('播放结束'); }); }, play_start(index, index2) { this.scrollList[index].child[index2].is_play = true this.$forceUpdate() }, play_end(index, index2) { this.scrollList[index].child[index2].is_play = false this.scrollList[index].child[index2].loading = false // 结束播放 不用加载了 this.$forceUpdate() }, swiper_change(e) { this.class_index = e.detail.current }, // 点击分类 click_class(index) { this.class_index = index }, } } </script> <style lang="scss" scoped> .header { padding: 20rpx; background-color: #FFDD09; .scroll-list { @include flex(column); padding-bottom: 0 !important; &__goods-item { margin-right: 40rpx; &__image { width: 80rpx; height: 80rpx; border-radius: 8rpx; display: block; } &__text { text-align: center; font-size: 28rpx; margin-top: 10rpx; display: inline-block; width: 100%; } .selected { color: white; } } &__show-more { background-color: #fff0f0; border-radius: 6rpx; padding: 6rpx 12rpx; @include flex(column); align-items: center; &__text { font-size: 24rpx; width: 24rpx; color: #f56c6c; line-height: 32rpx; } } } } .swiper { min-height: 80vh; .bgm_list { display: flex; flex-flow: row wrap; .selected { color: white; background-image: linear-gradient(120deg, #84fab0 0%, #8fd3f4 100%); } .bgm_item { box-sizing: border-box; flex-basis: 23.1%; margin-left: 10rpx; margin-top: 10rpx; padding: 20rpx 10rpx; border-radius: 10rpx; background-color: #F8F8F8; display: flex; flex-direction: column; justify-content: space-between; .title { text-align: center; } .icon { display: flex; justify-content: flex-end; } } } } </style>
代码说明:
在load_config中通过api获取配置,或者直接写死配置也可以的,只不过要改的话只能再重新提审了,其他js代码就是一些菜单点击的状态变化控制了,还有简单的页面样式css。
这篇分享文章就到这里啦!如果你对文章内容有疑问或想要深入讨论,欢迎在评论区留言,我会尽力回答。同时,如果你觉得这篇文章对你有帮助,不妨点个赞并分享给其他同学,让更多人受益。
想要了解更多相关知识,可以查看我以往的文章,其中有许多精彩内容。记得关注我,获取及时更新,我们可以一起学习、讨论技术,共同进步。
感谢你的阅读与支持,期待在未来的文章中与你再次相遇!
我的微信公众号:【xdub】,欢迎大家订阅,我会同步文章到公众号上。