当微信小程序官方提供的 audio 组件样式不满足我们的需求时,我们可以自定义一个音频播放器,由于不可抗力原因,我用uView框架中的 Popup 弹出层 实现了一个音频播放器弹框,大家根据需求做参考就好啦,话不多说,直接看效果吧~
1、效果图:暂停&播放
2、具体实现
2.1 准备按钮图片
2.2 使用组件
ps:下面代码中用到的示例mp3文件地址是从网上随便找的,如有侵权联系我删除,谢谢。
<template>
<MyAudio ref="myAudio" :show.sync="showAudio"/>
</template>
<script>
import MyAudio from "@/components/MyAudio";
export default {
components: {
MyAudio
},
data() {
return {
showAudio: false,
path: 'http://music.163.com/song/media/outer/url?id=447925558.mp3'
}
},
onLoad(options) {
this.showAudio = true
this.$refs.myAudio.init(this.path)
}
}
</script>
2.3 封装MyAudio组件
2.3.1 MyAudio.vue 文件
<template>
<u-popup
:show="show"
mode="center"
:closeable="true"
:safeAreaInsetBottom="false"
round="16rpx"
@close="clickMask"
>
<view class="audioPopup">
<view class="audioWra">
<view class="info">
<view class="title">音频播放器</view>
<view class="my_audio_content">
<AudioPlayer
ref="audioplayer"
startPic="https://img-blog.csdnimg.cn/direct/9a3eb3af5f2e466cb24acef01d684be3.png"
endPic="https://img-blog.csdnimg.cn/direct/ab1d7719ed024235af8144213cafa8bb.png"
></AudioPlayer>
</view>
<view class="audioBtnWra">
<view class="left" @click="close">关闭</view>
</view>
</view>
</view>
</view>
</u-popup>
</template>
<script>
import AudioPlayer from "./AudioPlayer";
export default {
components: {
AudioPlayer,
},
props: {
show: {
type: Boolean,
default: false,
},
isMaskClose: {
type: Boolean,
default: true,
},
isMove: {
type: Boolean,
default: false,
},
},
methods: {
init(monitorFileName) {
this.$refs.audioplayer.init(monitorFileName);
},
clickMask() {
if (!this.isMaskClose) return;
this.close();
},
close() {
this.$refs.audioplayer.refresh();
this.$emit("update:show", false);
},
},
};
</script>
<style scoped>
.mask {
position: fixed;
top: 0;
right: 0;
left: 0;
bottom: 0;
opacity: 0;
background-color: rgba(0, 0, 0, 0.6);
z-index: -1;
transition: all 0.3s ease-in-out 0s;
}
.mask-show {
z-index: 1000;
opacity: 1;
}
.audioPopup .audioWra {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
z-index: 9999;
}
.audioPopup .audioWra .info {
background: #ffffff;
width: 560rpx;
opacity: 1;
background: #ffffff;
border-radius: 16rpx;
animation: slide-scaer 0.2s linear;
padding-top: 24rpx;
box-sizing: border-box;
}
.audioPopup .audioWra .info .title {
font-size: 32rpx;
font-family: PingFangSC, PingFangSC-Medium;
font-weight: 500;
text-align: left;
color: #585858;
padding: 0rpx 24rpx;
text-align: center;
}
.audioPopup .audioWra .info .audioBtnWra {
display: flex;
align-items: center;
justify-content: space-between;
height: 100rpx;
line-height: 100rpx;
font-weight: 400;
}
.audioPopup .audioWra .info .audioBtnWra .left {
flex: 1;
color: #aaaaaa;
font-size: 28rpx;
height: 100%;
text-align: center;
}
.audioPopup .audioWra .info .audioBtnWra .right {
flex: 1;
color: #4088fe;
font-size: 28rpx;
height: 100%;
text-align: center;
}
@keyframes slide-scaer {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
.my_audio_content {
padding: 50rpx 24rpx 30rpx;
}
</style>
2.3.2 AudioPlayer.vue 文件
<template>
<!-- 音频播放器组件 -->
<view class="my-audio">
<image
class="audio-btn"
@click="doPause"
:src="startPic"
v-if="!status"
></image>
<image
class="audio-btn"
@click="doPlay"
:src="endPic"
v-if="status"
></image>
<view style="flex: 1">
<slider
@change="changeAudio"
class="audio-slider"
activeColor="#1790FF"
backgroundColor="#CDE5FF"
block-color="#1790FF"
blockSize="10"
:min="0"
:max="duration.toFixed(0)"
:value="currentTime.toFixed(0)"
:step="0.1"
></slider>
</view>
<view class="audio-time">
<span class="audio-current">{{ getTime(Math.round(currentTime)) }}</span>
<span class="audio-duration">/{{ getTime(Math.round(duration)) }}</span>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// url: null,
context: null,
currentTime: 0,
duration: 0,
status: false,
};
},
props: {
startPic: String,
endPic: String,
audioId: [String, Number],
},
methods: {
init(monitorFileName) {
this.context = uni.createInnerAudioContext();
this.context.src = monitorFileName;
this.onTimeUpdate();
this.onCanplay();
this.onEnded();
uni.$on("stop", (id) => {
if (id && id != this.audioId) {
this.context.stop();
this.status = false;
} else if (!id) {
this.context.stop();
this.status = false;
}
});
},
// 点击播放
doPlay() {
this.context.pause();
this.status = !this.status;
},
// 点击暂停
doPause() {
this.context.play();
this.status = !this.status;
},
// 进入可播放状态
onCanplay() {
this.context.onCanplay(() => {
var times = setInterval(() => {
this.duration = this.context.duration;
console.log(this.duration);
if (this.context.duration != 0) {
clearInterval(times);
}
}, 500);
});
},
// 音频播放进度
onTimeUpdate() {
this.context.onTimeUpdate(() => {
if (!Number.isFinite(this.context.duration)) {
this.duration = this.context.currentTime + 10;
this.currentTime = this.context.currentTime;
} else {
this.duration = this.context.duration;
this.currentTime = this.context.currentTime;
}
});
},
// 播放结束
onEnded() {
this.context.onEnded(() => {
this.status = false;
this.currentTime = 0;
});
},
changeAudio(e) {
let paused = this.context.paused;
this.context.pause();
this.context.seek(e.detail.value);
this.currentTime = e.detail.value;
if (!paused) {
this.context.play();
}
},
// 音频播放时间换算
getTime(time) {
var duration = parseInt(time);
var minute = parseInt(duration / 60);
var sec = (duration % 60) + "";
var isM0 = ":";
if (minute === 0) {
minute = "00";
} else if (minute < 10) {
minute = "0" + minute;
}
if (sec.length === 1) {
sec = "0" + sec;
}
return minute + isM0 + sec;
},
towNum(num) {
if (num >= 10) {
return num;
} else {
return "0" + num;
}
},
// 关闭时调用
refresh() {
this.status = false;
this.currentTime = 0;
this.context.stop();
},
},
};
</script>
<style lang="scss" scoped>
.my-audio {
width: 100%;
height: 96rpx;
background: #f0f7ff;
border-radius: 8rpx;
padding: 0 14rpx;
box-sizing: border-box;
display: flex;
align-items: center;
}
.audio-btn {
width: 60rpx;
height: 60rpx;
}
.audio-time {
font-size: 24rpx;
}
.audio-current {
color: #424b59;
}
.audio-duration {
color: #999999;
}
</style>