网上看到别的小程序播放音效还需要钱,定眼一看,这不很简单吗?一个分类、分类下放音频的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】,欢迎大家订阅,我会同步文章到公众号上。