- 组件结构
<div role="slider" class="pd-slider">
<div class="pd-slider__runway">
<div class="pd-slider__bar" style="<%= `width: ${value}%;`%> left: 0%;"></div>
<div class="pd-slider__button-wrapper" style="<%= `left: ${value}%`%>">
<div class="pd-slider__button"></div>
</div>
</div>
</div>
- css
.pd-slider:after,
.pd-slider:before {
display: table;
content: "";
}
.pd-slider:after {
clear: both;
}
.pd-slider .pd-slider__runway {
width: 100%;
height: 4px;
margin: 16px 0;
background-color: #e4e7ed;
border-radius: 3px;
position: relative;
cursor: pointer;
vertical-align: middle;
}
.pd-slider .pd-slider__bar {
height: 4px;
background-color: #25BC9E;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
position: absolute;
}
.pd-slider .pd-slider__button-wrapper {
height: 36px;
width: 36px;
position: absolute;
z-index: 1001;
top: -15px;
transform: translateX(-50%);
background-color: transparent;
text-align: center;
user-select: none;
line-height: normal;
}
.pd-slider .pd-slider__button {
width: 16px;
height: 16px;
border: 2px solid #25BC9E;
background-color: #fff;
border-radius: 50%;
transition: .2s;
user-select: none;
}
.pd-slider .pd-slider__button-wrapper:after {
content: "";
height: 100%;
}
.pd-slider .pd-slider__button-wrapper .pd-slider__button,
.pd-slider .pd-slider__button-wrapper:after {
display: inline-block;
vertical-align: middle;
}
.pd-slider .pd-slider__button:hover {
cursor: grab;
transform: scale(1.2);
}
.pd-slider .pd-slider__button-wrapper:hover {
cursor: grab;
}
- 代码逻辑部分
define([
'widgets/TabWidget',
'text!./slider.html'
], function (
TabWidget,
Template
) {
var widget = TabWidget.extend({
events: {
'mousedown .pd-slider__button': 'onButtonDown'
},
render: function (ops) {
this.option = Object.assign({
value,
step
}, ops)
this.dragging = false
this.startX = 0
this.currentX = 0
this.max = 100
this.min = 0
this.startPosition = 0
this.newPosition = null
this.$el.html(_.template(Template)({
...this.option
}));
return this
},
onButtonDown: function (e) {
e.preventDefault()
this.onDragStart(e)
const dom = this.$el
dom.on('mousemove', this.onDragging.bind(this))
dom.on('mouseup', this.onDragEnd.bind(this))
},
onDragging: function (event) {
if (this.dragging) {
let diff = 0;
this.currentX = event.clientX;
diff = (this.currentX - this.startX) / this.$el.find('.pd-slider__runway')[0].clientWidth * 100;
this.newPosition = this.startPosition + diff;
this.setPosition(this.newPosition);
}
},
onDragEnd() {
if (this.dragging) {
this.setPosition(this.newPosition);
this.trigger(
"on-slider-change",
this.newPosition
);
this.dragging = false;
const dom = this.$el
dom.off('mousemove', this.onDragging)
dom.off('mouseup', this.onDragEnd)
}
},
onDragStart: function (event) {
this.dragging = true;
this.startX = event.clientX;
this.startPosition = parseFloat(this.getCurrentPosition());
this.newPosition = this.startPosition;
},
setPosition(newPosition) {
if (newPosition === null || isNaN(newPosition)) return;
if (newPosition < 0) {
newPosition = 0;
} else if (newPosition > 100) {
newPosition = 100;
}
const lengthPerStep = 100 / ((this.max - this.min) / this.option.step);
const steps = Math.round(newPosition / lengthPerStep);
let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min;
value = parseFloat(value.toFixed(this.getPrecision()));
this.$el.find('.pd-slider__bar').css('width', `${value}%`)
this.$el.find('.pd-slider__button-wrapper').css('left', `${value}%`)
this.option.value = value
},
getPrecision: function () {
let precisions = [this.min, this.max, this.option.step].map(item => {
let decimal = ('' + item).split('.')[1];
return decimal ? decimal.length : 0;
});
return Math.max.apply(null, precisions);
},
getCurrentPosition: function () {
return `${(this.option.value - this.min) / (this.max - this.min) * 100}%`;
}
})
return widget
});