样式如图所示
<template>
<div class="audio-player">
<div class="player_top" flex-ac flex-justify-between >
<div class="fileName genericTitle" fs-28 l-height-32 height-64 pr-42 flex-ac>
<span class="text-line-2">{{ fileName }}</span>
</div>
<div class="play_btn">
<div class="toPlay" width-60 height-60 v-if="!playStatus" @click="onPlay"></div>
<div class="toStop" width-60 height-60 v-else @click="onPause"></div>
</div>
</div>
<div class="player_time" fs-14 mt-13 mb-8 flex flex-justify-between>
<span class="play_audioCurrent" style="color:#1e62d9"> {{ transTime(audioCurrent) }} </span>
<span class="play_audioDuration" style="color:#A5A5A5"> {{ transTime(audioDuration) }} </span>
</div>
<div class="play_progress" pl-13 pr-13>
<el-slider v-model="playProgress" :show-tooltip="false" @input="onProgressChange" />
</div>
</div>
<audio ref="audioRef" :src="url" @canplay="onCanplay" @timeupdate="updateProgress" @ended="playEnd" />
</template>
<script setup lang="ts">
import { ref, onBeforeMount, onUnmounted, onMounted, watch, nextTick } from 'vue';
const props = defineProps({
// 音频地址
url: {
type: String,
required: true
},
// 音频名称
fileName: {
type: String,
required: false
}
});
const emits = defineEmits(['play', 'timeupdate']);
watch(
() => props.url,
newVal => {
if (newVal) {
nextTick(() => {
initAudio();
});
}
}
);
const speedVisible = ref<boolean>(false); // 设置音频播放速度弹窗
const audioRef = ref<HTMLAudioElement | null>(null); // 音频标签对象
const activeSpeed = ref(1); // 音频播放速度
const audioDuration = ref(0); // 音频总时长
const audioCurrent = ref(0); // 音频当前播放时间
const audioVolume = ref(1); // 音频声音,范围 0-1
const playStatus = ref<boolean>(false); // 音频播放状态:true 播放,false 暂停
const playProgress = ref(0); // 音频播放进度
const initAudio = () => {
if (audioRef.value) {
audioRef.value.load();
playStatus.value = false;
playProgress.value = 0;
audioRef.value.src = props.url;
}
};
// 音频加载完毕的回调
const onCanplay = () => {
audioDuration.value = audioRef?.value.duration || 0;
};
const onPlay = async () => {
// 音频播放完后,重新播放
if (transTime(audioCurrent.value) === transTime(audioDuration.value)) audioRef.value.currentTime = 0;
await audioRef.value.play();
playStatus.value = true;
audioDuration.value = audioRef.value.duration;
emits('play');
};
const onPause = () => {
audioRef.value.pause();
playStatus.value = false;
};
// const onChangeSpeed = (value: number) => {
// activeSpeed.value = value;
// // 设置倍速
// audioRef.value.playbackRate = value;
// speedVisible.value = false;
// };
// const onHandleSpeed = () => {
// speedVisible.value = !speedVisible.value;
// };
// // 设置声音
// const onSetVolume = (value: number) => {
// audioRef.value.volume = value;
// audioVolume.value = value;
// };
// 音频播放时间换算
const transTime = (value: number) => {
let time = '';
let h = parseInt(String(value / 3600));
value %= 3600;
let m = parseInt(String(value / 60));
let s = parseInt(String(value % 60));
if (h > 0) {
time = formatTime(h + ':' + m + ':' + s);
} else {
time = formatTime(m + ':' + s);
}
return time;
};
// 格式化时间显示,补零对齐
const formatTime = (value: string) => {
let time = '';
let s = value.split(':');
let i = 0;
for (; i < s.length - 1; i++) {
time += s[i].length == 1 ? '0' + s[i] : s[i];
time += ':';
}
time += s[i].length == 1 ? '0' + s[i] : s[i];
return time;
};
const onTimeUpdate = () => {
if (audioRef.value) {
audioCurrent.value = audioRef.value.currentTime;
const progressPercentage = (audioRef.value.currentTime / audioRef.value.duration) * 100;
emits('timeupdate', {
currentTime: audioCurrent.value,
duration: audioDuration.value,
progress: progressPercentage
});
}
};
const onProgressChange = (value: number) => {
// if (!value) {
// return;
// }
console.log(value,'value');
audioRef.value.currentTime = (value / 100) * audioDuration.value;
};
const updateProgress = (e) => {
// console.log(e,'e');
onTimeUpdate();
const value = e.target.currentTime / e.target.duration;
if (audioRef.value.play) {
playProgress.value = value * 100;
audioCurrent.value = audioRef.value.currentTime;
}
};
const playEnd = () => {
playStatus.value = false;
};
// onMounted(() => {
// initAudio();
// });
onBeforeMount(() => {
});
onUnmounted(() => {
});
</script>
<style lang="scss" scoped>
.audio-player {
width: 100%;
height: 193px;
background: #ffffff;
border: 10px solid #c5e9ff;
border-radius: 20px;
padding: 26px 26px 0 26px;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
.player_top{
width: 100%;
}
.player_time{
width: 100%;
}
.play_progress{
width: 100%;
::v-deep(.el-slider__runway){
height: 10px;
border-radius: 6px;
background-color: #e5e5e5;
.el-slider__bar{
height: 10px;
border-radius: 6px;
background: linear-gradient(270deg,#53c0ff, #3870ff);
}
.el-slider__button-wrapper{
top: -13px;
}
.el-slider__button{
width: 26px;
height: auto !important;
aspect-ratio: 1 !important;
background-color: #1E62D9;
border: px2vw(4) solid #ffffff;
box-shadow: 0px 3px 6px 0px rgba(0,0,0,0.16);
}
}
}
.play-icon {
width: 60px;
height: 60px;
// margin-right: 7px;
cursor: pointer;
}
.play-time {
}
.play-progress {
width: 160px;
height: 4px;
background-color: #323547;
box-shadow: inset 0px 1px 0px 0px #20222d;
border-radius: 2px;
margin-right: 16px;
position: relative;
.play-current-progress {
height: 4px;
background: #00e5ff;
border-radius: 2px;
position: absolute;
top: 0;
left: 0;
}
}
.play-voice {
width: 20px;
height: 20px;
margin-right: 14px;
cursor: pointer;
}
.play-speed {
cursor: pointer;
color: #00e5ff;
}
.fileName {
}
.play_btn {
cursor: pointer;
.toPlay {
background: url('@/assets/images/icon_play_audio@2x.png') no-repeat;
background-size: 100% 100%;
background-origin: border-box;
background-clip: content-box;
&:hover {
background: url('@/assets/images/icon_play_audio_h@2x.png') no-repeat;
background-size: 100% 100%;
background-origin: border-box;
background-clip: content-box;
}
}
.toStop {
background: url('@/assets/images/icon_stop_audio@2x.png') no-repeat;
background-size: 100% 100%;
background-origin: border-box;
background-clip: content-box;
&:hover {
background: url('@/assets/images/icon_stop_audio_h@2x.png') no-repeat;
background-size: 100% 100%;
background-origin: border-box;
background-clip: content-box;
}
}
}
}
</style>
使用
<AudioPlayer
:url="currentResource?.resourceUrl"
:fileName="currentResource?.resourceName"
@play="playMedia"
@timeupdate="toUpdatePlayMediaTime"
/>
// 音视频触发播放
const playMedia = () => {
// console.log(currentResource.value, 'playMedia开始播放');
};
// 音视频播放进度
const toUpdatePlayMediaTime = e => {
if (e.progress > 85 && currentResource.value.completeStatus === 0) {
// 音视频播放进度大于85则该资源标记为学习完成
}
};