You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
6.6 KiB
Vue

2 years ago
<template>
<div
class="el-slider__button-wrapper"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
@mousedown="onButtonDown"
@touchstart="onButtonDown"
:class="{ 'hover': hovering, 'dragging': dragging }"
:style="wrapperStyle"
ref="button"
tabindex="0"
@focus="handleMouseEnter"
@blur="handleMouseLeave"
@keydown.left="onLeftKeyDown"
@keydown.right="onRightKeyDown"
@keydown.down.prevent="onLeftKeyDown"
@keydown.up.prevent="onRightKeyDown"
>
<el-tooltip
placement="top"
ref="tooltip"
:popper-class="tooltipClass"
:disabled="!showTooltip">
<span slot="content">{{ formatValue }}</span>
<div class="el-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }"></div>
</el-tooltip>
</div>
</template>
<script>
import ElTooltip from 'element-ui/packages/tooltip';
export default {
name: 'ElSliderButton',
components: {
ElTooltip
},
props: {
value: {
type: Number,
default: 0
},
vertical: {
type: Boolean,
default: false
},
tooltipClass: String
},
data() {
return {
hovering: false,
dragging: false,
isClick: false,
startX: 0,
currentX: 0,
startY: 0,
currentY: 0,
startPosition: 0,
newPosition: null,
oldValue: this.value
};
},
computed: {
disabled() {
return this.$parent.sliderDisabled;
},
max() {
return this.$parent.max;
},
min() {
return this.$parent.min;
},
step() {
return this.$parent.step;
},
showTooltip() {
return this.$parent.showTooltip;
},
precision() {
return this.$parent.precision;
},
currentPosition() {
return `${ (this.value - this.min) / (this.max - this.min) * 100 }%`;
},
enableFormat() {
return this.$parent.formatTooltip instanceof Function;
},
formatValue() {
return this.enableFormat && this.$parent.formatTooltip(this.value) || this.value;
},
wrapperStyle() {
return this.vertical ? { bottom: this.currentPosition } : { left: this.currentPosition };
}
},
watch: {
dragging(val) {
this.$parent.dragging = val;
}
},
methods: {
displayTooltip() {
this.$refs.tooltip && (this.$refs.tooltip.showPopper = true);
},
hideTooltip() {
this.$refs.tooltip && (this.$refs.tooltip.showPopper = false);
},
handleMouseEnter() {
this.hovering = true;
this.displayTooltip();
},
handleMouseLeave() {
this.hovering = false;
this.hideTooltip();
},
onButtonDown(event) {
if (this.disabled) return;
event.preventDefault();
this.onDragStart(event);
window.addEventListener('mousemove', this.onDragging);
window.addEventListener('touchmove', this.onDragging);
window.addEventListener('mouseup', this.onDragEnd);
window.addEventListener('touchend', this.onDragEnd);
window.addEventListener('contextmenu', this.onDragEnd);
},
onLeftKeyDown() {
if (this.disabled) return;
this.newPosition = parseFloat(this.currentPosition) - this.step / (this.max - this.min) * 100;
this.setPosition(this.newPosition);
this.$parent.emitChange();
},
onRightKeyDown() {
if (this.disabled) return;
this.newPosition = parseFloat(this.currentPosition) + this.step / (this.max - this.min) * 100;
this.setPosition(this.newPosition);
this.$parent.emitChange();
},
onDragStart(event) {
this.dragging = true;
this.isClick = true;
if (event.type === 'touchstart') {
event.clientY = event.touches[0].clientY;
event.clientX = event.touches[0].clientX;
}
if (this.vertical) {
this.startY = event.clientY;
} else {
this.startX = event.clientX;
}
this.startPosition = parseFloat(this.currentPosition);
this.newPosition = this.startPosition;
},
onDragging(event) {
if (this.dragging) {
this.isClick = false;
this.displayTooltip();
this.$parent.resetSize();
let diff = 0;
if (event.type === 'touchmove') {
event.clientY = event.touches[0].clientY;
event.clientX = event.touches[0].clientX;
}
if (this.vertical) {
this.currentY = event.clientY;
diff = (this.startY - this.currentY) / this.$parent.sliderSize * 100;
} else {
this.currentX = event.clientX;
diff = (this.currentX - this.startX) / this.$parent.sliderSize * 100;
}
this.newPosition = this.startPosition + diff;
this.setPosition(this.newPosition);
}
},
onDragEnd() {
if (this.dragging) {
/*
* 防止在 mouseup 后立即触发 click导致滑块有几率产生一小段位移
* 不使用 preventDefault 是因为 mouseup click 没有注册在同一个 DOM
*/
setTimeout(() => {
this.dragging = false;
this.hideTooltip();
if (!this.isClick) {
this.setPosition(this.newPosition);
this.$parent.emitChange();
}
}, 0);
window.removeEventListener('mousemove', this.onDragging);
window.removeEventListener('touchmove', this.onDragging);
window.removeEventListener('mouseup', this.onDragEnd);
window.removeEventListener('touchend', this.onDragEnd);
window.removeEventListener('contextmenu', this.onDragEnd);
}
},
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.step);
const steps = Math.round(newPosition / lengthPerStep);
let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min;
value = parseFloat(value.toFixed(this.precision));
this.$emit('input', value);
this.$nextTick(() => {
this.displayTooltip();
this.$refs.tooltip && this.$refs.tooltip.updatePopper();
});
if (!this.dragging && this.value !== this.oldValue) {
this.oldValue = this.value;
}
}
}
};
</script>