<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>slider</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimum-scale=1.0, viewport-fit=cover"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<script src="https://cdn.staticfile.org/vue/3.2.36/vue.global.min.js"></script>
</head>
<style>
.slider-box {
width: 300px;
margin: 0 auto;
height: 10px;
border-radius: 20px;
background: #ddd;
position: relative;
}
.slider-nei {
position: absolute;
left: 0;
top: 0;
width: 0;
height: 10px;
border-radius: 20px;
background: #FF6F91;
}
.point-box {
width: 20px;
height: 20px;
border-radius: 50%;
background: yellowgreen;
position: absolute;
top: -5px;
}
.point-box:hover {
cursor: grab;
}
.zhua:active {
cursor: grabbing;
}
.transtion {
transition: all .3s;
}
</style>
<body>
<div id="app">
<div class="slider-box" @mousedown="handleClickZong">
<div :class="['slider-nei',isClick?'transtion':'']" :style="{'width':progress+'%','left':`${(moveStart/290*100)}%`}"></div>
<div :style="{'transform':`translateX(${moveDistance}px) ${isMove?'scale(1.2) ':'scale(1)'}`}"
:class="{
'point-box':true,
'zhua':isMove,
'transtion':isClick
}"
v-if="!range"
@mousedown.stop.prevent="handleMousedown">
</div>
<div :style="{'transform':`translateX(${moveDistanceOne}px) ${isMove?'scale(1.2) ':'scale(1)'}`}"
:class="{
'point-box':true,
'zhua':isMove,
'transtion':isClick
}"
v-if="range"
@mousedown.stop.prevent="handleMousedownOne">
</div>
<div :style="{'transform':`translateX(${moveDistanceTwo}px) ${isMove?'scale(1.2) ':'scale(1)'}`}"
:class="{
'point-box':true,
'zhua':isMove,
'transtion':isClick
}"
v-if="range"
@mousedown.stop.prevent="handleMousedownTwo">
</div>
</div>
</div>
</body>
<script>
const app = {
data() {
return {
range: false,//是否开启双滑块
isClick: false,//是否直接点击
isMove: false, //是否处于滑动状态
progress: 0,//进度条宽度
moveDistance: 0,
initX: 0,//记录鼠标点下时的坐标,用于移动中求差值
moveStart: 0,//进度条的开始位置
moveEnd: 0,//进度条的结束位置
moveDistanceOne: 0,//滑块一的位置
moveDistanceTwo: 0,//滑块二的位置
}
},
methods: {
//移动端 touchstart 点击监听 ontouchmove 移动监听 ontouchend 松开监听,e.changedTouches[0] 元素偏移的一些数据
//PC端 mousedown 点击监听 onmousemove 移动监听 onmouseup 松开监听
/**
* 初始坐标就是鼠标点下时的clientX坐标,这样就可以做到将slider放在页面任何位置,拖动原点偏移的量也是正确的,
* 因为原点移动距离是用鼠标移动的位置和鼠标点下的位置做差值计算,所以不用担心这里的clientX会因为slider放在
* 别的地方而导致距离计算错误
* @param e
*/
//鼠标点击
handleMousedown(e) {
this.isMove = true
this.initX = e.clientX
const sliderDomWidth = document.getElementsByClassName('slider-box')[0].clientWidth-20
document.onmousemove = (e) => {
if (this.isMove) {
this.moveDistance += e.clientX - this.initX
this.initX = e.clientX
if ((this.moveDistance / sliderDomWidth) * 100 > 100) {
this.moveDistance = sliderDomWidth
this.progress = 100
return
}
if ((this.moveDistance / sliderDomWidth) * 100 < 0) {
this.moveDistance = 0
this.progress = 0
return
}
this.progress = (this.moveDistance / sliderDomWidth) * 100
}
}
document.onmouseup = (e) => {
this.isMove = false
document.onmousemove = null
}
},
/**
* 当点击进度条某一个位置时,快捷设置进度条的位置,这里就需要用offsetX而不是clientX了。这里需要设置的便宜距离是鼠标距离目标元素的距离而不是鼠标在整个页面上的坐标
* @param e
*/
handleClickZong(e) {
this.isClick = true
setTimeout(() => {
this.isClick = false
}, 300)
const sliderDomWidth = document.getElementsByClassName('slider-box')[0].clientWidth-20
this.moveDistance = e.offsetX - 10
this.progress = (this.moveDistance / sliderDomWidth) * 100
},
//滑动滑块1
handleMousedownOne(e) {
this.isMove = true
let currentDistance = e.clientX
const sliderDomWidth = document.getElementsByClassName('slider-box')[0].clientWidth-20
document.onmousemove = (e) => {
if (this.isMove) {
const moveX = e.clientX - currentDistance
currentDistance = e.clientX
this.moveDistanceOne += moveX
if (this.moveDistanceOne > sliderDomWidth) {
this.moveDistanceOne = sliderDomWidth
return
}
if (this.moveDistanceOne < 0) {
this.moveDistanceOne = 0
return
}
this.moveStart = this.moveDistanceOne < this.moveDistanceTwo ? this.moveDistanceOne : this.moveDistanceTwo
this.moveEnd = this.moveDistanceOne > this.moveDistanceTwo ? this.moveDistanceOne : this.moveDistanceTwo
this.progress = parseInt(((this.moveEnd - this.moveStart) / sliderDomWidth) * 100)
}
}
document.onmouseup = (e) => {
this.isMove = false
document.onmousemove = null
}
},
//滑动滑块2
handleMousedownTwo(e) {
this.isMove = true
let currentDistance = e.clientX
const sliderDomWidth = document.getElementsByClassName('slider-box')[0].clientWidth-20
document.onmousemove = (e) => {
if (this.isMove) {
const moveX = e.clientX - currentDistance
currentDistance = e.clientX
this.moveDistanceTwo += moveX
if (this.moveDistanceTwo > sliderDomWidth) {
this.moveDistanceTwo = sliderDomWidth
return
}
if (this.moveDistanceTwo < 0) {
this.moveDistanceTwo = 0
return
}
this.moveStart = this.moveDistanceOne < this.moveDistanceTwo ? this.moveDistanceOne : this.moveDistanceTwo
this.moveEnd = this.moveDistanceOne > this.moveDistanceTwo ? this.moveDistanceOne : this.moveDistanceTwo
this.progress = parseInt(((this.moveEnd - this.moveStart) / sliderDomWidth) * 100)
}
}
document.onmouseup = (e) => {
this.isMove = false
document.onmousemove = null
}
}
}
}
Vue.createApp(app).mount('#app')
</script>
</html>
功能比较简单,就讲讲实现逻辑。
1.首先是监听鼠标按下,然后表示开始移动,接着去监听mousemove,可以得到鼠标移动的距离,根据这个距离就可以动态的设置小滑块的位置了,然后在松开的时候清除监听。(ps:细心的同学应该注意到了这里鼠标按下的事件是监听的滑块,但是鼠标移动和鼠标松开却是在整个页面上监听。这么做是因为如果只监听鼠标在滑块内移动的话,鼠标移动太快就会移出滑块,导致逻辑上出现错误,而监听整个页面的移动则可以实现鼠标在滑块处点击,然后只要不松开,鼠标移动到任意位置都能影响到滑块,而非只能在滑块里移动)
2.单滑块的实现就比较简单,拿到滑块的移动距离后和整个进度条的宽度求一个百分比就可以得出内部有颜色的进度条的百分比。然后就是对一些临界值做一些拦截就行了。
3.组件里同时也封装了双滑块,双滑块要比单滑块稍微难一点点。具体逻辑如下:
双滑块需要准备四个变量,滑块一的位置,滑块二的位置,进度条的起始位置,进度条的结束位置。
在滑动某一个滑块时,和滑动单滑块的逻辑是相同的,只不过在记录值的时候稍微有一点变化,这里起始位置和结束位置根据滑块一的位置和滑块二的位置时时变化,谁大谁就是起始位置,谁小谁就是结束位置。而计算进度条的宽度就用结束位置减去起始位置然后除以总长度得出百分比。
还有一点,在两个滑块滑动的过程中,需要动态的去定位进度条的位置,以达到进度条始终都在两个滑块中间的效果。定位的值可以用起始位置和总宽度求百分比得到。
代码写的比较糙有很多待完善待优化的地方,但是实现逻辑大概就是这样,仅供参考
网友评论