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