UNPKG

6.77 kBPlain TextView Raw
1<template>
2 <div
3 class="el-slider__button-wrapper"
4 @mouseenter="handleMouseEnter"
5 @mouseleave="handleMouseLeave"
6 @mousedown="onButtonDown"
7 @touchstart="onButtonDown"
8 :class="{ 'hover': hovering, 'dragging': dragging }"
9 :style="wrapperStyle"
10 ref="button"
11 tabindex="0"
12 @focus="handleMouseEnter"
13 @blur="handleMouseLeave"
14 @keydown.left="onLeftKeyDown"
15 @keydown.right="onRightKeyDown"
16 @keydown.down.prevent="onLeftKeyDown"
17 @keydown.up.prevent="onRightKeyDown"
18 >
19 <el-tooltip
20 placement="top"
21 ref="tooltip"
22 :popper-class="tooltipClass"
23 :disabled="!showTooltip">
24 <span slot="content">{{ formatValue }}</span>
25 <div class="el-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }"></div>
26 </el-tooltip>
27 </div>
28</template>
29
30<script>
31 import ElTooltip from 'element-ui/packages/tooltip';
32
33 export default {
34 name: 'ElSliderButton',
35
36 components: {
37 ElTooltip
38 },
39
40 props: {
41 value: {
42 type: Number,
43 default: 0
44 },
45 vertical: {
46 type: Boolean,
47 default: false
48 },
49 tooltipClass: String
50 },
51
52 data() {
53 return {
54 hovering: false,
55 dragging: false,
56 isClick: false,
57 startX: 0,
58 currentX: 0,
59 startY: 0,
60 currentY: 0,
61 startPosition: 0,
62 newPosition: null,
63 oldValue: this.value
64 };
65 },
66
67 computed: {
68 disabled() {
69 return this.$parent.sliderDisabled;
70 },
71
72 max() {
73 return this.$parent.max;
74 },
75
76 min() {
77 return this.$parent.min;
78 },
79
80 step() {
81 return this.$parent.step;
82 },
83
84 showTooltip() {
85 return this.$parent.showTooltip;
86 },
87
88 precision() {
89 return this.$parent.precision;
90 },
91
92 currentPosition() {
93 return `${ (this.value - this.min) / (this.max - this.min) * 100 }%`;
94 },
95
96 enableFormat() {
97 return this.$parent.formatTooltip instanceof Function;
98 },
99
100 formatValue() {
101 return this.enableFormat && this.$parent.formatTooltip(this.value) || this.value;
102 },
103
104 wrapperStyle() {
105 return this.vertical ? { bottom: this.currentPosition } : { left: this.currentPosition };
106 }
107 },
108
109 watch: {
110 dragging(val) {
111 this.$parent.dragging = val;
112 }
113 },
114
115 methods: {
116 displayTooltip() {
117 this.$refs.tooltip && (this.$refs.tooltip.showPopper = true);
118 },
119
120 hideTooltip() {
121 this.$refs.tooltip && (this.$refs.tooltip.showPopper = false);
122 },
123
124 handleMouseEnter() {
125 this.hovering = true;
126 this.displayTooltip();
127 },
128
129 handleMouseLeave() {
130 this.hovering = false;
131 this.hideTooltip();
132 },
133
134 onButtonDown(event) {
135 if (this.disabled) return;
136 event.preventDefault();
137 this.onDragStart(event);
138 window.addEventListener('mousemove', this.onDragging);
139 window.addEventListener('touchmove', this.onDragging);
140 window.addEventListener('mouseup', this.onDragEnd);
141 window.addEventListener('touchend', this.onDragEnd);
142 window.addEventListener('contextmenu', this.onDragEnd);
143 },
144 onLeftKeyDown() {
145 if (this.disabled) return;
146 this.newPosition = parseFloat(this.currentPosition) - this.step / (this.max - this.min) * 100;
147 this.setPosition(this.newPosition);
148 this.$parent.emitChange();
149 },
150 onRightKeyDown() {
151 if (this.disabled) return;
152 this.newPosition = parseFloat(this.currentPosition) + this.step / (this.max - this.min) * 100;
153 this.setPosition(this.newPosition);
154 this.$parent.emitChange();
155 },
156 onDragStart(event) {
157 this.dragging = true;
158 this.isClick = true;
159 if (event.type === 'touchstart') {
160 event.clientY = event.touches[0].clientY;
161 event.clientX = event.touches[0].clientX;
162 }
163 if (this.vertical) {
164 this.startY = event.clientY;
165 } else {
166 this.startX = event.clientX;
167 }
168 this.startPosition = parseFloat(this.currentPosition);
169 this.newPosition = this.startPosition;
170 },
171
172 onDragging(event) {
173 if (this.dragging) {
174 this.isClick = false;
175 this.displayTooltip();
176 this.$parent.resetSize();
177 let diff = 0;
178 if (event.type === 'touchmove') {
179 event.clientY = event.touches[0].clientY;
180 event.clientX = event.touches[0].clientX;
181 }
182 if (this.vertical) {
183 this.currentY = event.clientY;
184 diff = (this.startY - this.currentY) / this.$parent.sliderSize * 100;
185 } else {
186 this.currentX = event.clientX;
187 diff = (this.currentX - this.startX) / this.$parent.sliderSize * 100;
188 }
189 this.newPosition = this.startPosition + diff;
190 this.setPosition(this.newPosition);
191 }
192 },
193
194 onDragEnd() {
195 if (this.dragging) {
196 /*
197 * 防止在 mouseup 后立即触发 click,导致滑块有几率产生一小段位移
198 * 不使用 preventDefault 是因为 mouseup 和 click 没有注册在同一个 DOM 上
199 */
200 setTimeout(() => {
201 this.dragging = false;
202 this.hideTooltip();
203 if (!this.isClick) {
204 this.setPosition(this.newPosition);
205 this.$parent.emitChange();
206 }
207 }, 0);
208 window.removeEventListener('mousemove', this.onDragging);
209 window.removeEventListener('touchmove', this.onDragging);
210 window.removeEventListener('mouseup', this.onDragEnd);
211 window.removeEventListener('touchend', this.onDragEnd);
212 window.removeEventListener('contextmenu', this.onDragEnd);
213 }
214 },
215
216 setPosition(newPosition) {
217 if (newPosition === null || isNaN(newPosition)) return;
218 if (newPosition < 0) {
219 newPosition = 0;
220 } else if (newPosition > 100) {
221 newPosition = 100;
222 }
223 const lengthPerStep = 100 / ((this.max - this.min) / this.step);
224 const steps = Math.round(newPosition / lengthPerStep);
225 let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min;
226 value = parseFloat(value.toFixed(this.precision));
227 this.$emit('input', value);
228 this.$nextTick(() => {
229 this.displayTooltip();
230 this.$refs.tooltip && this.$refs.tooltip.updatePopper();
231 });
232 if (!this.dragging && this.value !== this.oldValue) {
233 this.oldValue = this.value;
234 }
235 }
236 }
237 };
238</script>