通过鼠标点击,移动事件来模拟一个滑块组件
实现的效果
无step平滑效果
有step间隔效果
实现逻辑
实现结构及样式
直接实现DOM和style就好了
<div id="sliderBox" class="slider-box">
<span class="slider" id="slider"></span>
</div>
.slider-box{
margin: 50px;
width: 500px;
height: 4px;
background-color: #eee;
border-radius: 4px;
position: relative;
}
.slider{
position: absolute;
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
background: blue;
top: -4px;
left: 0;
}
模拟滑动
思路
- 通过对slider监听mousedown,来设置一个标识,表明开始滑动滑块
- 通过对document监听mousemove,来获取鼠标的位置,计算并设置滑块的坐标left
- 通过对document监听mouseup,来改变标识,表明鼠标松开-停止滑动
此处通过mousedown, mousemove, mouseup事件来模拟滑动的过程。下面就是最初的实现逻辑,可以实现平滑的效果
(function () {
const sliderBox = document.getElementById('sliderBox');
const span = document.getElementById('slider');
const boxWidth = sliderBox.offsetWidth;
// set a boolean that true is drag slider and false is other event;
let flag = false;
// mousedown event of slider
function onMouseDown(event) {
flag = true;
}
// mousemove event of document
function onMouseMove(event) {
if (flag) {
// get mouse offsetX for box
const offsetX = event.x - sliderBox.offsetLeft;
// get left offset
let left = 0;
if (offsetX > boxWidth) {
left = boxWidth;
} else if (offsetX >= 0) {
left = offsetX;
} else {
left = 0;
}
// set left position of slider
span.style.left = left + 'px';
}
}
// mouseup event of document
function onMouseUp(event) {
flag = false;
}
// add listener when page or component loaded
function addListener() {
span.addEventListener('mousedown', onMouseDown);
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
}
// remove listener when page or component destroy
function removeListener() {
span.removeEventListener('mousedown', onMouseDown);
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
}
addListener();
})()
模拟step
思路
- 根据sliderBox的宽度和步骤的配置计算出每一个step的大小
- 根据四舍五入的原则计算出每次滑动的当前step值
- 根据计算出的当前值设置滑块的位置
通过设置min,max,以及step来模拟有间隔的滑动,下面是最终逻辑,相比较第一步有所调整,进行了简单封装
使用DOM
<div id="sliderBox" class="slider-box"></div>
css
.slider-box{
margin: 50px;
width: 500px;
height: 4px;
background-color: #eee;
border-radius: 4px;
position: relative;
}
.slider {
position: absolute;
display: inline-block;
width: 12px;
height: 12px;
border-radius: 6px;
background-color: blue;
top: -4px;
left: -6px;
}
调用js-实现有间隔的滑动
(function () {
new Slider({
boxId: 'sliderBox',// slider box id
stepEnable: true,// enable step
min: 0,
max: 30,
step: 1,
leftOff: 6,// slider left offset
value: 10,// default value
onValueChange: function(current){
// do something when value is changed
console.log('current', current);
}
});
})()
slider类
class Slider {
constructor(option) {
if (!option || !option.boxId) {
throw new Error('Slider needed boxId');
}
this.boxId = option.boxId;
this.onValueChange = option.onValueChange;
this.stepEnable = !option.stepEnable ? false : true;
this.flag = false;
this.min = option.min || 0;
this.max = option.max || 10;
this.step = option.step || 1;
this.leftOff = option.leftOff || 0;
this.value = option.value || 0;
this.init();
}
init() {
this.render();
this.addListener();
}
render() {
// get sliderBox
this.sliderBox = document.getElementById(this.boxId);
// create slider
const slider = document.createElement('span');
slider.classList.add('slider');
this.slider = slider;
this.sliderBox.appendChild(this.slider);
// get slider-box width
this.boxWidth = this.sliderBox.offsetWidth;
// get slider-box left
this.boxLeft = this.sliderBox.getBoundingClientRect().left;
this.setLeftByValue();
}
// set slider default position with default value when init render
setLeftByValue(){
const left = ((this.value / this.max)*this.boxWidth).toFixed(2);
this.slider.style.left = (left - this.leftOff) + 'px';
}
// get current value and slider left position
getCurrentValue(left){
let current = (left/this.boxWidth) * this.max;
current = Math.round(current/this.step);
current = current * this.step;
return {
current,
left: this.stepEnable ? (current/this.max)*this.boxWidth.toFixed(2) : left,
}
}
// add listener when page or component loaded
addListener() {
this.slider.addEventListener('mousedown', this.onMouseDown);
document.addEventListener('mousemove', this.onMouseMove);
document.addEventListener('mouseup', this.onMouseUp);
}
// remove listener when page or component destroy
removeListener() {
this.slider.removeEventListener('mousedown', this.onMouseDown);
document.removeEventListener('mousemove', this.onMouseMove);
document.removeEventListener('mouseup', this.onMouseUp);
}
// mousedown event of slider
onMouseDown = () => {
this.flag = true;
}
// mousemove event of document
onMouseMove = (event) => {
if (this.flag) {
// get mouse offsetX for box
const offsetX = event.clientX - this.boxLeft;
// get left offset
let left = 0;
if (offsetX > this.boxWidth) {
left = this.boxWidth;
} else if (offsetX >= 0) {
left = offsetX;
} else {
left = 0;
}
const result = this.getCurrentValue(left);
if(this.onValueChange){
// send to onValueChange function when it exist
if(result.current !== this.value){
// emit change event when value is different
this.value = result.current;
this.onValueChange(result.current);
}
}
// set left position of slider
this.slider.style.left = (result.left - this.leftOff) + 'px';
}
}
// mouseup event of document
onMouseUp = () => {
this.flag = false;
}
destroy() {
this.removeListener();
}
}