2025-04-19 15:38:48 +08:00

239 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>