问题描述:
slider通过点击可定位到相应的位置(或者特别快速的拖动,快速拖动个人认为slider组件应该是利用了mousedown和mouseup并结合定时器从而实现拖拽效果,当特别快速的拖动时,只要没有达到定时器设定的时间间隔,故和鼠标点击是相同效果),但是在用鼠标拖拽进度条时会失败(超过一定时间),进度条会回弹到之前时间点附近,达不到那种可以滑动转到指定区域的功能。
以下为部分相关源码:
<el-slider
v-model="percent"
:format-tooltip="formatTooltip"
@change="changeProgress"
></el-slider>
<audio
ref="audio"
:src="currentSong.url"
@pause="audioPaused"
@timeupdate="updateTime"
@ended="audioEnd"
></audio>
data() {
return {
// 进度条当前的百分百(即currentTime/duration)
percent: 0,
// 当前播放歌曲的时间点
currentTime: 0,
// 是否静音
isMuted: false,
// 默认音量
volume: 0.5,
// 拖动条默认值
volumeNum: 50,
// 歌词界面是否打开
showLyric: false,
};
},
methods: {
// 监听播放时间改变
updateTime(e) {
this.currentTime =
(e.target.currentTime / this.currentSong.duration) * 100;
},
// 改变播放进度
changeProgress(per) {
const audio = this.$refs.audio;
audio.currentTime = (per / 100) * this.currentSong.duration;
},
}
问题分析:
根据elementUI文档可知,slider组件的change事件使用鼠标拖曳时,只在松开鼠标后触发。根据源码写法可知:当前时长是随着audio的更新而赋值达到实时更新的,而element的slider的change方法是在鼠标松开后才会触发,这样产生的实际情况则是拖动进度条期间,在鼠标松开之前,audio的timeUpdate事件一直在触发,currentTime其实是实时更新的,再次松开鼠标时,changeProgress方法中的audio.currentTime的值并不是鼠标拖拽到的指定进度 ,而是audio的timeupdate事件中的方法赋值给当前时长的进度值,故此会产生进度条回弹效果。
解决思路 :
在鼠标按下时把audio的timeupdate事件方法禁止,即暂停记录实时的currentTime,在拖拽后鼠标松开的时候解禁timeupdate事件方法,同时把slider组件change事件得到的时间赋值给data中的currentTime,这样就能保证进度条的点就是当前播放的时间点。所以添加progressState参数判断是否在拖拽状态,以此来达到目的。
部分源码如下:
<el-slider
v-model="percent"
:format-tooltip="formatTooltip"
@change="changeProgress"
@mousedown.native="progressState = true"
@mouseup.native="progressState = false"
></el-slider>
<audio
ref="audio"
:src="currentSong.url"
@pause="audioPaused"
@timeupdate="updateTime"
@ended="audioEnd"
></audio>
data() {
return {
// 进度条当前的百分百(即currentTime/duration)
percent: 0,
// 当前播放歌曲的时间点
currentTime: 0,
// 是否静音
isMuted: false,
// 默认音量
volume: 0.5,
// 拖动条默认值
volumeNum: 50,
// 歌词界面是否打开
showLyric: false,
// slider是否处于拖动状态 解决鼠标拖拽slider滑块滑动到指定位置无效
progressState: false,
}
}
methods: {
// 监听播放时间改变
updateTime(e) {
if (!this.progressState) {
this.currentTime = e.target.currentTime;
this.percent = (e.target.currentTime / this.currentSong.duration) * 100;
}
},
// 拖动进度条改变播放进度时(鼠标拖拽松开时触发)
changeProgress(per) {
// this.progressState = true;
const audio = this.$refs.audio;
const currentTime = (per / 100) * this.currentSong.duration;
this.currentTime = currentTime;
this.percent = per;
audio.currentTime = currentTime;
},
}
2022.04.02更新:
上述解决问题还遗留一个bug,因为在el-slider组件中绑定了mouseup事件,当拖拽松开鼠标时,若鼠标离开了slider组件区域(即slider相关的盒子大小区域),会导致mouseup事件触发不了,从而processState一直处于true状态,即认为处于拖动状态,故进度掉会停止向前移动。此处解决思路之一是对mouseup事件进行监听,如下:(不一定必须是window对象,也可是比slider更大盒子的dom元素。或者也可把mouseup事件绑定在更高级的父元素上。暂时解决bug了,但此处解决的还并不完美,日后再完善)
<el-slider
v-model="percent"
:format-tooltip="formatTooltip"
@change="changeProgress"
@mousedown.native="progressState = true"
@mouseup.native="changeProgressState"
></el-slider>
<audio
ref="audio"
:src="currentSong.url"
@pause="audioPaused"
@timeupdate="updateTime"
@ended="audioEnd"
></audio>
data() {
return {
// 进度条当前的百分百(即currentTime/duration)
percent: 0,
// 当前播放歌曲的时间点
currentTime: 0,
// 是否静音
isMuted: false,
// 默认音量
volume: 0.5,
// 拖动条默认值
volumeNum: 50,
// 歌词界面是否打开
showLyric: false,
// slider是否处于拖动状态 解决鼠标拖拽slider滑块滑动到指定位置无效
progressState: false,
}
}
methods: {
// 监听播放时间改变
updateTime(e) {
if (!this.progressState) {
this.currentTime = e.target.currentTime;
this.percent = (e.target.currentTime / this.currentSong.duration) * 100;
}
},
// 拖动进度条改变播放进度时(鼠标拖拽松开时触发)
changeProgress(per) {
// this.progressState = true;
const audio = this.$refs.audio;
const currentTime = (per / 100) * this.currentSong.duration;
this.currentTime = currentTime;
this.percent = per;
audio.currentTime = currentTime;
},
changeProgressState(e) {
this.progressState = false;
},
}
mounted() {
// 监听slider中的mouseup事件,防止拖拽进度条松开时,鼠标离开特定区域造成事件丢失不生效
window.addEventListener("mouseup", this.changeProgressState);
},