问题:鼠标点击或者拖动slider滑块到指定位置修改音乐播放进度偶尔会失效(失效概率更大)
原因:我是用slider change事件去修改音乐播放进度的,element plus官方文档说明change事件是值改变时触发(使用鼠标拖拽时,只在松开鼠标后触发)即松开鼠标才会触发change事件;同时,我slider的进度是通过audio的更新timeupdate去赋值到slider的值达到实时更新的。当我在按下鼠标但还没松开时,还未触发change事件修改进度,audio的timeupdate方法就再次触发了,当我松开鼠标时,change事件里面赋的值就不是鼠标点击/拖拽的指定进度,而是audio的timeupdate方法的进度值,就导致点击/拖拽slider修改音乐进度失效
之前的关键代码:
//html 部分
<audio
ref="audioRef"
@pause="onPause"
@play="onPlay"
@timeupdate="onTimeupdate"
@loadedmetadata="onLoadedmetadata"
@ended="onTimeOver"
:src="MusicUrl"
></audio>
<el-slider
v-model="currentTime"
:format-tooltip="audioFormatterTooltip"
:max="totalTime"
@change="changeAudioCurrentTime"
/>
//js 部分
const audioRef = ref()
const totalTime = ref()
const currentTime = ref()
const audioType = ref(false)
const audioFormatterTooltip = val => {
return formatTime(val)
}
const onPause = () => {
audioType.value = false
}
const onPlay = () => {
audioType.value = true
}
const onTimeupdate = e => {
currentTime.value = parseInt(e.target.currentTime)
}
const onLoadedmetadata = e => {
totalTime.value = parseInt(e.target.duration)
}
const onTimeOver = () => {
audioType.value = false
currentTime.value = 0
}
const playAudio = () => {
audioRef.value.play()
}
const pauseAudio = () => {
audioRef.value.pause()
}
const handleToggleAudio = () => {
return audioType.value ? pauseAudio() : playAudio()
}
const changeAudioCurrentTime = val => {
console.log(val)
audioRef.value.currentTime = val
}
解决办法:在鼠标按下时将audio的timeupdate方法禁止掉,在松开的时候再触发,所以添加一个参数来判断是否禁止该事件的时间更新
修改的代码:
//html
<el-slider
v-model="currentTime"
:format-tooltip="audioFormatterTooltip"
:max="totalTime"
@mousedown.native="mouseDown"
@mouseup.native="mouseUp"
@change="changeAudioCurrentTime"
/>
//js
// 判断是否在更新slider
const isTimeupdate = ref(false)
const onPause = () => {
audioType.value = false
isTimeupdate.value = false
}
const onPlay = () => {
audioType.value = true
isTimeupdate.value = true
}
const onTimeupdate = e => {
if (isTimeupdate.value === false) {//当在slider按下鼠标时,slider的数据不跟着audio播放进度进行更新
return
}
currentTime.value = parseInt(e.target.currentTime)
}
const mouseDown = () => {//按下停止更新
isTimeupdate.value = false
}
const mouseUp = () => {//松开继续更新
isTimeupdate.value = true
}
之前这个问题困恼了我很久,在网上找了很多文档都没有真正解决,是看了这个文档 vue+element制作音乐播放器播放进度条bug(鼠标拖拽slider滑块滑动到指定位置无效) 才解决的,特此记录一下,防止之后碰到相似问题不知如何解决
完整代码:
<script setup>
import { ref, onMounted } from 'vue'
import MusicUrl from '@/assets/mp3/Russ-Psycho,Pt.2.mp3' //引入音乐
import { formatTime } from '@util/format' //格式化时间
const audioRef = ref()
const totalTime = ref()
const currentTime = ref()
const audioType = ref(false)
// 判断是否在更新
const isTimeupdate = ref(true)
const audioFormatterTooltip = val => {//slider tooltip格式化
return formatTime(val)
}
const onPause = () => {//audio 停止时
audioType.value = false
}
const onPlay = () => {//audio 播放时
audioType.value = true
}
const onTimeupdate = e => {//audio timeupdate 时间更新
if (isTimeupdate.value === false) {//依据isTimeupdate的值判断是否将audio当前进度更新到slider
return
}
currentTime.value = parseInt(e.target.currentTime)
}
const onLoadedmetadata = e => {//audio 加载完成
totalTime.value = parseInt(e.target.duration)
}
const onTimeOver = () => {//audio 播放完成
audioType.value = false
currentTime.value = 0
}
const playAudio = () => {//播放audio
audioRef.value.play()
}
const pauseAudio = () => {//暂停audio
audioRef.value.pause()
}
const handleToggleAudio = () => {//依据播放状态判断是 播放/暂停audio
return audioType.value ? pauseAudio() : playAudio()
}
const changeAudioCurrentTime = val => {//slider change事件 将slider进度赋值给audio
console.log(val)
audioRef.value.currentTime = val
}
const mouseDown = () => {//slider 鼠标按下 停止slider进度更新
isTimeupdate.value = false
}
const mouseUp = () => {//slider 鼠标按下 恢复slider进度更新
isTimeupdate.value = true
}
onMounted(() => {
})
</script>
<template>
<div class="audio-item-wrapper">
<div class="audio-top">
<div class="name">歌曲名称歌曲名称歌曲名称歌曲名称歌曲名称</div>
<div class="delete"></div>
</div>
<div class="audio-control">
<div
class="button"
:class="audioType === true ? 'pause' : 'play'"
@click="handleToggleAudio"
></div>
<audio
ref="audioRef"
@pause="onPause"
@play="onPlay"
@timeupdate="onTimeupdate"
@loadedmetadata="onLoadedmetadata"
@ended="onTimeOver"
:src="MusicUrl"
></audio>
<div class="control-wrapper">
<div class="control">
<el-slider
v-model="currentTime"
:format-tooltip="audioFormatterTooltip"
:max="totalTime"
@mousedown.native="mouseDown"
@mouseup.native="mouseUp"
@change="changeAudioCurrentTime"
/>
</div>
<div class="time">{{ formatTime(totalTime) }}</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.audio-item-wrapper {
width: 510px;
height: 110px;
background: #ffffff;
border: 1px solid #ebeaef;
border-radius: 10px;
padding: 20px;
//cursor: pointer;
&.active,
&:hover {
border: 1px solid #005aff;
.audio-top {
.name {
color: #005aff;
}
.delete {
display: block;
}
}
}
.audio-top {
display: flex;
justify-content: space-between;
width: 100%;
height: 20px;
.name {
font-size: 20px;
color: #374051;
}
.delete {
display: none;
width: 20px;
height: 20px;
background-image: url('@img/space/delete.png');
cursor: pointer;
}
}
.audio-control {
display: flex;
align-items: center;
height: 30px;
margin-top: 20px;
.button {
width: 30px;
height: 30px;
cursor: pointer;
&.play {
background-image: url('@img/space/play.png');
}
&.pause {
background-image: url('@img/space/pause.png');
}
}
}
}
.control-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
width: calc(100% - 30px - 12px);
margin-left: 12px;
.control {
width: 378px;
//height: 30px;
height: 100%;
//background: red;
}
.time {
//width: 39px;
font-size: 16px;
color: #727d91;
}
}
</style>
<style lang="scss">
.el-slider {
height: 30px;
}
.el-slider__runway {
//滑块进度条 整个
width: 378px;
height: 6px;
background: #f4f6fa;
border: 1px solid #ebeaef;
border-radius: 3px;
}
.el-slider__bar {
//进度
height: 6px;
background: #005aff;
border-radius: 3px;
}
.el-slider__button {
// 拖动的滑块的样式
width: 12px;
height: 12px;
background: #005aff;
border-radius: 50%;
border: none;
}
</style>
//format.js
export function formatTime(s) {
let h
h = Math.floor(s / 60)
s = s % 60
h += ''
s += ''
h = h.length === 1 ? '0' + h : h
s = s.length === 1 ? '0' + s : s
if (isNaN(h)) {
return '00:00'
}
return h + ':' + s
}
几个事件 解释 在代码处的用法
ended 在媒体播放到尽头发生此事件 =>audio @ended
pause 当媒体被用户暂停或以编程方式暂停时,发生此事件 =>audio @pause
play 当媒体已启动或以不再暂停时,发生此事件 =>audio @play
loadedmetadata 加载元数据(比如尺寸和持续时间)时,发生此事件 =>audio @loadedmetadata
timeupdate 当播放位置更改时发生此事件 =>audio @updatetime
change 当form元素的内容、选择的内容或选中的状态发生改变时,发生此事件 =>el-slider @change
mousedown 当用户在元素上按下鼠标按钮时,发生此事件 =>el-slider @mousedown.native
mouseup 当用户在元素上释放鼠标按钮时,发生此事件 =>el-slider @mouseup.native