<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.$refs.tooltip && this.$refs.tooltip.updatePopper(); }); if (!this.dragging && this.value !== this.oldValue) { this.oldValue = this.value; } } } }; </script>