实现自定义aduio播放器
在项目中有两款样式,有些细节上的区分,但是整体api
相似
样式不进行细节的讲解和展示。
使用vue3
,提取useAduio
hooks进行复用。
原理:
利用audio 的回调和属性来进行音频的播放、时间调整等。
canplay
:音频加载,可以播放handleTimeUpdate
:更新时间end
:播放结束,更换播放按钮.play()
:播放.pause()
:暂停.currentTime
:当前时间
template
代码:
<template>
<div class="audio-container j-between flex">
<div class="flex-1">
//时间
<div class="j-between flex">
<span>{{ currentTime }}</span>
<span class="mgl-6">{{ duration }}</span>
</div>
//播放线条
<div class="audio-setbacks" ref="setbacksBox" @click="handleBacks">
<i class="audio-this-setbacks" :style="{ width: progress + '%' }">
<span class="audio-backs-btn"></span>
</i>
<span class="audio-cache-setbacks" :style="{ width: progress + '%' }">
</span>
</div>
</div>
//播放按钮
<img src="@/assets/home/play.png" v-if="!isPlay" class="play-icon mgl-18" @click="handlePlay" />
<img src="@/assets/home/p.png" v-else class="play-icon mgl-18" @click="handlePlay" />
</div>
//不展示的aduio用来获取视频长度等
<audio ref="aduio" :src="url" style="display: none;" class="custom-audio" @canplay="handleCanPlay"
@ended="handleEnd" @timeupdate="handleTimeUpdate" controls="controls">
Your browser does not support the audio element.
</audio>
</template>
<script setup>
import useAduio from '@/hooks/aduio.js';
import { defineProps } from 'vue';
const props = defineProps({
url: {
type: String,
default: ''
}
});
const { aduio, setbacksBox, currentTime, progress, duration, isPlay, handlePlay, handleBacks, handleTimeUpdate, handleCanPlay, handleEnd } = useAduio();
</script>
<style>
//有些样式代码没有复制上来,基于工程有些通用性css,可自行调整下样式。
audio,
audio source {
width: 100%;
background: #DFFFFA;
border-radius: 8px 8px 8px 8px;
}
.audio-setbacks {
height: 2px;
border-radius: 3px;
background-color: #CDE1DD;
cursor: pointer;
width: 100%;
position: relative;
margin-top: 6px;
}
.audio-cache-setbacks,
.audio-this-setbacks {
height: 100%;
position: absolute;
left: 0;
top: 0;
border-radius: 3px;
}
.audio-cache-setbacks {
width: 0;
background-color: #FFA358;
z-index: 1;
cursor: pointer;
transition: width 0.3s ease;
-webkit-transition: width 0.3s ease;
}
.audio-this-setbacks {
background-color: #FFA358;
}
.audio-backs-btn {
position: absolute;
right: -3px;
margin-top: -2px;
width: 6px;
height: 6px;
background-color: #FFA358;
border-radius: 50%;
cursor: pointer;
}
.audio-cache-setbacks,
.audio-this-setbacks {
height: 100%;
position: absolute;
left: 0;
top: 0;
border-radius: 3px;
}
</style>
hooks
import { ref, onMounted } from 'vue';
export default function useAduio() {
const aduio = ref(null);
const setbacksBox = ref(null)
const currentTime = ref(0);
const progress = ref(0)
const duration = ref(0);
const isPlay = ref(false)
// 初始化
onMounted(() => {
setTimeout(() => {
handleTimeUpdate()
}, 200);
})
/**
* 能够播放时的回调
* @param {Event} event 事件对象
*/
const handleCanPlay = (event) => {
// 更新当前时间
handleTimeUpdate()
// 播放
handlePlay()
}
// 格式化时间
function formatSeconds(seconds) {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
const formattedMinutes = String(minutes).padStart(2, '0');
const formattedSeconds = String(remainingSeconds).padStart(2, '0');
return `${formattedMinutes}:${formattedSeconds}`;
}
// 点击时间
const handleBacks = (event) => {
const childRect = setbacksBox.value.getBoundingClientRect();
const clickX = event.clientX - childRect.left;
let progressValue = (clickX / childRect.width)
let value = (progressValue * 100)
progress.value = value
let time = progressValue * aduio.value.duration
currentTime.value = formatSeconds(Math.floor(time));
aduio.value.currentTime = time
handlePlay()
}
//更新时间
const handleTimeUpdate = () => {
let curTime = aduio.value?.currentTime ?? 0
let durTime = isNaN(aduio.value?.duration ?? 0) ? 0 : aduio.value?.duration ?? 0
currentTime.value = formatSeconds(Math.floor(curTime));
duration.value = formatSeconds(Math.floor(durTime));
progress.value = (Math.floor(curTime) / Math.floor(durTime)) * 100
};
/**
* 播放/暂停
* @description 如果当前是暂停状态,就播放,否则暂停
* @param {Event} event 事件对象
*/
const handlePlay = (event) => {
if (aduio.value.paused) {
// 如果当前是暂停状态,就播放
aduio.value.play().catch(error => {
console.error(error);
});
isPlay.value = true
} else {
// 如果当前不是暂停状态,就暂停
aduio.value.pause();
isPlay.value = false
}
}
const handleEnd = () => {
handlePlay()
console.log('播放结束')
}
return {
aduio, setbacksBox, currentTime, progress, duration, isPlay, handlePlay, handleBacks, handleTimeUpdate, handleCanPlay, handleEnd
}
}