mirror of https://github.com/ElemeFE/element
Slider: add range support (#2751)
parent
7f6d698f72
commit
450cf81ded
|
@ -7,7 +7,8 @@
|
||||||
value3: 42,
|
value3: 42,
|
||||||
value4: 0,
|
value4: 0,
|
||||||
value5: 0,
|
value5: 0,
|
||||||
value6: 0
|
value6: 0,
|
||||||
|
value7: [4, 8]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,6 +120,35 @@ Set value via a input box.
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### Range selection
|
||||||
|
|
||||||
|
Selecting a range of values is supported.
|
||||||
|
|
||||||
|
:::demo Setting the `range` attribute activates range mode, where the binding value is an array made up of two boundary values.
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<el-slider
|
||||||
|
v-model="value7"
|
||||||
|
range
|
||||||
|
show-stops
|
||||||
|
:max="10">
|
||||||
|
</el-slider>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value7: [4, 8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
## Attributes
|
## Attributes
|
||||||
| Attribute | Description | Type | Accepted Values | Default |
|
| Attribute | Description | Type | Accepted Values | Default |
|
||||||
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
||||||
|
@ -126,9 +156,10 @@ Set value via a input box.
|
||||||
| max | maximum value | number | — | 100 |
|
| max | maximum value | number | — | 100 |
|
||||||
| disabled | whether Slider is disabled | boolean | — | false |
|
| disabled | whether Slider is disabled | boolean | — | false |
|
||||||
| step | step size | number | — | 1 |
|
| step | step size | number | — | 1 |
|
||||||
| show-input | whether to display an input box | boolean | — | false |
|
| show-input | whether to display an input box, works when `range` is false | boolean | — | false |
|
||||||
| show-input-controls | whether to display control buttons when `show-input` is true | boolean | — | true |
|
| show-input-controls | whether to display control buttons when `show-input` is true | boolean | — | true |
|
||||||
| show-stops | whether to display breakpoints | boolean | — | false |
|
| show-stops | whether to display breakpoints | boolean | — | false |
|
||||||
|
| range | whether to select a range | boolean | — | false |
|
||||||
|
|
||||||
## Events
|
## Events
|
||||||
| Event Name | Description | Parameters |
|
| Event Name | Description | Parameters |
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
value3: 42,
|
value3: 42,
|
||||||
value4: 0,
|
value4: 0,
|
||||||
value5: 0,
|
value5: 0,
|
||||||
value6: 0
|
value6: 0,
|
||||||
|
value7: [4, 8]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,6 +144,35 @@
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### 范围选择
|
||||||
|
|
||||||
|
支持选择某一数值范围
|
||||||
|
|
||||||
|
:::demo 设置`range`即可开启范围选择,此时绑定值是一个数组,其元素分别为最小边界值和最大边界值
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<el-slider
|
||||||
|
v-model="value7"
|
||||||
|
range
|
||||||
|
show-stops
|
||||||
|
:max="10">
|
||||||
|
</el-slider>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value7: [4, 8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
### Attributes
|
### Attributes
|
||||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||||
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
||||||
|
@ -150,9 +180,10 @@
|
||||||
| max | 最大值 | number | — | 100 |
|
| max | 最大值 | number | — | 100 |
|
||||||
| disabled | 是否禁用 | boolean | — | false |
|
| disabled | 是否禁用 | boolean | — | false |
|
||||||
| step | 步长 | number | — | 1 |
|
| step | 步长 | number | — | 1 |
|
||||||
| show-input | 是否显示输入框 | boolean | — | false |
|
| show-input | 是否显示输入框,仅在非范围选择时有效 | boolean | — | false |
|
||||||
| show-input-controls | 在显示输入框的情况下,是否显示输入框的控制按钮 | boolean | — | true|
|
| show-input-controls | 在显示输入框的情况下,是否显示输入框的控制按钮 | boolean | — | true|
|
||||||
| show-stops | 是否显示间断点 | boolean | — | false |
|
| show-stops | 是否显示间断点 | boolean | — | false |
|
||||||
|
| range | 是否为范围选择 | boolean | — | false |
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
| 事件名称 | 说明 | 回调参数 |
|
| 事件名称 | 说明 | 回调参数 |
|
||||||
|
|
|
@ -0,0 +1,156 @@
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="el-slider__button-wrapper"
|
||||||
|
@mouseenter="handleMouseEnter"
|
||||||
|
@mouseleave="handleMouseLeave"
|
||||||
|
@mousedown="onButtonDown"
|
||||||
|
:class="{ 'hover': hovering, 'dragging': dragging }"
|
||||||
|
:style="{ left: currentPosition }"
|
||||||
|
ref="button">
|
||||||
|
<el-tooltip placement="top" ref="tooltip">
|
||||||
|
<span slot="content">{{ value }}</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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
hovering: false,
|
||||||
|
dragging: false,
|
||||||
|
startX: 0,
|
||||||
|
currentX: 0,
|
||||||
|
startPosition: 0,
|
||||||
|
newPosition: null,
|
||||||
|
oldValue: this.value
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
disabled() {
|
||||||
|
return this.$parent.disabled;
|
||||||
|
},
|
||||||
|
|
||||||
|
max() {
|
||||||
|
return this.$parent.max;
|
||||||
|
},
|
||||||
|
|
||||||
|
min() {
|
||||||
|
return this.$parent.min;
|
||||||
|
},
|
||||||
|
|
||||||
|
step() {
|
||||||
|
return this.$parent.step;
|
||||||
|
},
|
||||||
|
|
||||||
|
precision() {
|
||||||
|
return this.$parent.precision;
|
||||||
|
},
|
||||||
|
|
||||||
|
currentPosition() {
|
||||||
|
return `${ (this.value - this.min) / (this.max - this.min) * 100 }%`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
dragging(val) {
|
||||||
|
this.$parent.dragging = val;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
showTooltip() {
|
||||||
|
this.$refs.tooltip && (this.$refs.tooltip.showPopper = true);
|
||||||
|
},
|
||||||
|
|
||||||
|
hideTooltip() {
|
||||||
|
this.$refs.tooltip && (this.$refs.tooltip.showPopper = false);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleMouseEnter() {
|
||||||
|
this.hovering = true;
|
||||||
|
this.showTooltip();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleMouseLeave() {
|
||||||
|
this.hovering = false;
|
||||||
|
this.hideTooltip();
|
||||||
|
},
|
||||||
|
|
||||||
|
onButtonDown(event) {
|
||||||
|
if (this.disabled) return;
|
||||||
|
this.onDragStart(event);
|
||||||
|
window.addEventListener('mousemove', this.onDragging);
|
||||||
|
window.addEventListener('mouseup', this.onDragEnd);
|
||||||
|
window.addEventListener('contextmenu', this.onDragEnd);
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragStart(event) {
|
||||||
|
this.dragging = true;
|
||||||
|
this.startX = event.clientX;
|
||||||
|
this.startPosition = parseInt(this.currentPosition, 10);
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragging(event) {
|
||||||
|
if (this.dragging) {
|
||||||
|
this.showTooltip();
|
||||||
|
this.currentX = event.clientX;
|
||||||
|
const diff = (this.currentX - this.startX) / this.$parent.$sliderWidth * 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();
|
||||||
|
this.setPosition(this.newPosition);
|
||||||
|
}, 0);
|
||||||
|
window.removeEventListener('mousemove', this.onDragging);
|
||||||
|
window.removeEventListener('mouseup', this.onDragEnd);
|
||||||
|
window.removeEventListener('contextmenu', this.onDragEnd);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setPosition(newPosition) {
|
||||||
|
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.$refs.tooltip && this.$refs.tooltip.updatePopper();
|
||||||
|
if (!this.dragging && this.value !== this.oldValue) {
|
||||||
|
this.oldValue = this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -2,9 +2,8 @@
|
||||||
<div class="el-slider">
|
<div class="el-slider">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="inputValue"
|
v-model="inputValue"
|
||||||
v-if="showInput"
|
v-if="showInput && !range"
|
||||||
class="el-slider__input"
|
class="el-slider__input"
|
||||||
@keyup.native="onInputChange"
|
|
||||||
ref="input"
|
ref="input"
|
||||||
:step="step"
|
:step="step"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
|
@ -15,29 +14,37 @@
|
||||||
</el-input-number>
|
</el-input-number>
|
||||||
<div class="el-slider__runway"
|
<div class="el-slider__runway"
|
||||||
:class="{ 'show-input': showInput, 'disabled': disabled }"
|
:class="{ 'show-input': showInput, 'disabled': disabled }"
|
||||||
@click="onSliderClick" ref="slider">
|
@click="onSliderClick"
|
||||||
<div class="el-slider__bar" :style="{ width: currentPosition }"></div>
|
ref="slider">
|
||||||
<div
|
<div
|
||||||
class="el-slider__button-wrapper"
|
class="el-slider__bar"
|
||||||
@mouseenter="handleMouseEnter"
|
:style="{
|
||||||
@mouseleave="handleMouseLeave"
|
width: barWidth,
|
||||||
@mousedown="onButtonDown"
|
left: barLeft
|
||||||
:class="{ 'hover': hovering, 'dragging': dragging }"
|
}">
|
||||||
:style="{left: currentPosition}"
|
</div>
|
||||||
ref="button">
|
<slider-button
|
||||||
<el-tooltip placement="top" ref="tooltip">
|
v-model="firstValue"
|
||||||
<span slot="content">{{ value }}</span>
|
ref="button1">
|
||||||
<div class="el-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }"></div>
|
</slider-button>
|
||||||
</el-tooltip>
|
<slider-button
|
||||||
|
v-model="secondValue"
|
||||||
|
ref="button2"
|
||||||
|
v-if="range">
|
||||||
|
</slider-button>
|
||||||
|
<div
|
||||||
|
class="el-slider__stop"
|
||||||
|
v-for="item in stops"
|
||||||
|
:style="{ 'left': item + '%' }"
|
||||||
|
v-if="showStops">
|
||||||
</div>
|
</div>
|
||||||
<div class="el-slider__stop" v-for="item in stops" :style="{ 'left': item + '%' }" v-if="showStops"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script type="text/babel">
|
<script type="text/babel">
|
||||||
import ElInputNumber from 'element-ui/packages/input-number';
|
import ElInputNumber from 'element-ui/packages/input-number';
|
||||||
import ElTooltip from 'element-ui/packages/tooltip';
|
import SliderButton from './button.vue';
|
||||||
import { getStyle } from 'element-ui/src/utils/dom';
|
import { getStyle } from 'element-ui/src/utils/dom';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -56,12 +63,8 @@
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 1
|
default: 1
|
||||||
},
|
},
|
||||||
defaultValue: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
value: {
|
value: {
|
||||||
type: Number,
|
type: [Number, Array],
|
||||||
default: 0
|
default: 0
|
||||||
},
|
},
|
||||||
showInput: {
|
showInput: {
|
||||||
|
@ -79,142 +82,136 @@
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
range: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
ElInputNumber,
|
ElInputNumber,
|
||||||
ElTooltip
|
SliderButton
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
firstValue: null,
|
||||||
|
secondValue: null,
|
||||||
|
oldValue: null,
|
||||||
precision: 0,
|
precision: 0,
|
||||||
inputValue: null,
|
inputValue: null,
|
||||||
timeout: null,
|
dragging: false
|
||||||
hovering: false,
|
|
||||||
dragging: false,
|
|
||||||
startX: 0,
|
|
||||||
currentX: 0,
|
|
||||||
startPos: 0,
|
|
||||||
newPos: null,
|
|
||||||
oldValue: this.value,
|
|
||||||
currentPosition: (this.value - this.min) / (this.max - this.min) * 100 + '%'
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
inputValue(val) {
|
inputValue(val) {
|
||||||
this.$emit('input', Number(val));
|
this.firstValue = val;
|
||||||
},
|
},
|
||||||
|
|
||||||
value(val) {
|
value(val, oldVal) {
|
||||||
this.$nextTick(() => {
|
if (this.dragging ||
|
||||||
this.updatePopper();
|
Array.isArray(val) &&
|
||||||
});
|
Array.isArray(oldVal) &&
|
||||||
if (typeof val !== 'number' || isNaN(val) || val < this.min) {
|
val.every((item, index) => item === oldVal[index])) {
|
||||||
this.$emit('input', this.min);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (val > this.max) {
|
this.setValues();
|
||||||
this.$emit('input', this.max);
|
},
|
||||||
return;
|
|
||||||
|
dragging(val) {
|
||||||
|
if (!val) {
|
||||||
|
this.setValues();
|
||||||
}
|
}
|
||||||
this.inputValue = val;
|
},
|
||||||
this.setPosition((val - this.min) * 100 / (this.max - this.min));
|
|
||||||
|
firstValue(val) {
|
||||||
|
if (this.range) {
|
||||||
|
this.$emit('input', [this.minValue, this.maxValue]);
|
||||||
|
} else {
|
||||||
|
this.inputValue = val;
|
||||||
|
this.$emit('input', val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
secondValue() {
|
||||||
|
if (this.range) {
|
||||||
|
this.$emit('input', [this.minValue, this.maxValue]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
min() {
|
||||||
|
this.setValues();
|
||||||
|
},
|
||||||
|
|
||||||
|
max() {
|
||||||
|
this.setValues();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
handleMouseEnter() {
|
valueChanged() {
|
||||||
this.hovering = true;
|
if (this.range) {
|
||||||
this.$refs.tooltip.showPopper = true;
|
return ![this.minValue, this.maxValue]
|
||||||
},
|
.every((item, index) => item === this.oldValue[index]);
|
||||||
|
} else {
|
||||||
handleMouseLeave() {
|
return this.value !== this.oldValue;
|
||||||
this.hovering = false;
|
|
||||||
this.$refs.tooltip.showPopper = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
updatePopper() {
|
|
||||||
this.$refs.tooltip.updatePopper();
|
|
||||||
},
|
|
||||||
|
|
||||||
setPosition(newPos) {
|
|
||||||
if (newPos < 0) {
|
|
||||||
newPos = 0;
|
|
||||||
} else if (newPos > 100) {
|
|
||||||
newPos = 100;
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
const lengthPerStep = 100 / ((this.max - this.min) / this.step);
|
setValues() {
|
||||||
const steps = Math.round(newPos / lengthPerStep);
|
const val = this.value;
|
||||||
let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min;
|
if (this.range && Array.isArray(val)) {
|
||||||
value = parseFloat(value.toFixed(this.precision));
|
if (val[1] < this.min) {
|
||||||
this.$emit('input', value);
|
this.$emit('input', [this.min, this.min]);
|
||||||
this.currentPosition = (this.value - this.min) / (this.max - this.min) * 100 + '%';
|
} else if (val[0] > this.max) {
|
||||||
if (!this.dragging) {
|
this.$emit('input', [this.max, this.max]);
|
||||||
if (this.value !== this.oldValue) {
|
} else if (val[0] < this.min) {
|
||||||
this.$emit('change', this.value);
|
this.$emit('input', [this.min, val[1]]);
|
||||||
this.oldValue = this.value;
|
} else if (val[1] > this.max) {
|
||||||
|
this.$emit('input', [val[0], this.max]);
|
||||||
|
} else {
|
||||||
|
this.firstValue = val[0];
|
||||||
|
this.secondValue = val[1];
|
||||||
|
if (this.valueChanged()) {
|
||||||
|
this.$emit('change', [this.minValue, this.maxValue]);
|
||||||
|
this.oldValue = val.slice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!this.range && typeof val === 'number' && !isNaN(val)) {
|
||||||
|
if (val < this.min) {
|
||||||
|
this.$emit('input', this.min);
|
||||||
|
} else if (val > this.max) {
|
||||||
|
this.$emit('input', this.max);
|
||||||
|
} else {
|
||||||
|
this.firstValue = val;
|
||||||
|
if (this.valueChanged()) {
|
||||||
|
this.$emit('change', val);
|
||||||
|
this.oldValue = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setPosition(percent) {
|
||||||
|
const targetValue = this.min + percent * (this.max - this.min) / 100;
|
||||||
|
if (!this.range) {
|
||||||
|
this.$refs.button1.setPosition(percent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let button;
|
||||||
|
if (Math.abs(this.minValue - targetValue) < Math.abs(this.maxValue - targetValue)) {
|
||||||
|
button = this.firstValue < this.secondValue ? 'button1' : 'button2';
|
||||||
|
} else {
|
||||||
|
button = this.firstValue > this.secondValue ? 'button1' : 'button2';
|
||||||
|
}
|
||||||
|
this.$refs[button].setPosition(percent);
|
||||||
|
},
|
||||||
|
|
||||||
onSliderClick(event) {
|
onSliderClick(event) {
|
||||||
if (this.disabled || this.dragging) return;
|
if (this.disabled || this.dragging) return;
|
||||||
const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
|
const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
|
||||||
this.setPosition((event.clientX - sliderOffsetLeft) / this.$sliderWidth * 100);
|
this.setPosition((event.clientX - sliderOffsetLeft) / this.$sliderWidth * 100);
|
||||||
},
|
|
||||||
|
|
||||||
onInputChange() {
|
|
||||||
if (this.value === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!isNaN(this.value)) {
|
|
||||||
this.setPosition((this.value - this.min) * 100 / (this.max - this.min));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onDragStart(event) {
|
|
||||||
this.dragging = true;
|
|
||||||
this.startX = event.clientX;
|
|
||||||
this.startPos = parseInt(this.currentPosition, 10);
|
|
||||||
},
|
|
||||||
|
|
||||||
onDragging(event) {
|
|
||||||
if (this.dragging) {
|
|
||||||
this.$refs.tooltip.showPopper = true;
|
|
||||||
this.currentX = event.clientX;
|
|
||||||
const diff = (this.currentX - this.startX) / this.$sliderWidth * 100;
|
|
||||||
this.newPos = this.startPos + diff;
|
|
||||||
this.setPosition(this.newPos);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onDragEnd() {
|
|
||||||
if (this.dragging) {
|
|
||||||
/*
|
|
||||||
* 防止在 mouseup 后立即触发 click,导致滑块有几率产生一小段位移
|
|
||||||
* 不使用 preventDefault 是因为 mouseup 和 click 没有注册在同一个 DOM 上
|
|
||||||
*/
|
|
||||||
setTimeout(() => {
|
|
||||||
this.dragging = false;
|
|
||||||
this.$refs.tooltip.showPopper = false;
|
|
||||||
this.setPosition(this.newPos);
|
|
||||||
}, 0);
|
|
||||||
window.removeEventListener('mousemove', this.onDragging);
|
|
||||||
window.removeEventListener('mouseup', this.onDragEnd);
|
|
||||||
window.removeEventListener('contextmenu', this.onDragEnd);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onButtonDown(event) {
|
|
||||||
if (this.disabled) return;
|
|
||||||
this.onDragStart(event);
|
|
||||||
window.addEventListener('mousemove', this.onDragging);
|
|
||||||
window.addEventListener('mouseup', this.onDragEnd);
|
|
||||||
window.addEventListener('contextmenu', this.onDragEnd);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -224,31 +221,67 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
stops() {
|
stops() {
|
||||||
const stopCount = (this.max - this.value) / this.step;
|
const stopCount = (this.max - this.min) / this.step;
|
||||||
const currentLeft = parseFloat(this.currentPosition);
|
|
||||||
const stepWidth = 100 * this.step / (this.max - this.min);
|
const stepWidth = 100 * this.step / (this.max - this.min);
|
||||||
const result = [];
|
const result = [];
|
||||||
for (let i = 1; i < stopCount; i++) {
|
for (let i = 1; i < stopCount; i++) {
|
||||||
result.push(currentLeft + i * stepWidth);
|
result.push(i * stepWidth);
|
||||||
}
|
}
|
||||||
return result;
|
if (this.range) {
|
||||||
|
return result.filter(step => {
|
||||||
|
return step < 100 * (this.minValue - this.min) / (this.max - this.min) ||
|
||||||
|
step > 100 * (this.maxValue - this.min) / (this.max - this.min);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return result.filter(step => step > 100 * (this.firstValue - this.min) / (this.max - this.min));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
minValue() {
|
||||||
|
return Math.min(this.firstValue, this.secondValue);
|
||||||
|
},
|
||||||
|
|
||||||
|
maxValue() {
|
||||||
|
return Math.max(this.firstValue, this.secondValue);
|
||||||
|
},
|
||||||
|
|
||||||
|
barWidth() {
|
||||||
|
return this.range
|
||||||
|
? `${ 100 * (this.maxValue - this.minValue) / (this.max - this.min) }%`
|
||||||
|
: `${ 100 * (this.firstValue - this.min) / (this.max - this.min) }%`;
|
||||||
|
},
|
||||||
|
|
||||||
|
barLeft() {
|
||||||
|
return this.range
|
||||||
|
? `${ 100 * (this.minValue - this.min) / (this.max - this.min) }%`
|
||||||
|
: '0%';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
mounted() {
|
||||||
if (typeof this.value !== 'number' ||
|
if (this.range) {
|
||||||
isNaN(this.value) ||
|
if (Array.isArray(this.value)) {
|
||||||
this.value < this.min) {
|
this.firstValue = Math.max(this.min, this.value[0]);
|
||||||
this.$emit('input', this.min);
|
this.secondValue = Math.min(this.max, this.value[1]);
|
||||||
} else if (this.value > this.max) {
|
} else {
|
||||||
this.$emit('input', this.max);
|
this.firstValue = this.min;
|
||||||
|
this.secondValue = this.max;
|
||||||
|
}
|
||||||
|
this.oldValue = [this.firstValue, this.secondValue];
|
||||||
|
} else {
|
||||||
|
if (typeof this.value !== 'number' || isNaN(this.value)) {
|
||||||
|
this.firstValue = this.min;
|
||||||
|
} else {
|
||||||
|
this.firstValue = Math.min(this.max, Math.max(this.min, this.value));
|
||||||
|
}
|
||||||
|
this.oldValue = this.firstValue;
|
||||||
}
|
}
|
||||||
let precisions = [this.min, this.max, this.step].map(item => {
|
let precisions = [this.min, this.max, this.step].map(item => {
|
||||||
let decimal = ('' + item).split('.')[1];
|
let decimal = ('' + item).split('.')[1];
|
||||||
return decimal ? decimal.length : 0;
|
return decimal ? decimal.length : 0;
|
||||||
});
|
});
|
||||||
this.precision = Math.max.apply(null, precisions);
|
this.precision = Math.max.apply(null, precisions);
|
||||||
this.inputValue = this.inputValue || this.value;
|
this.inputValue = this.inputValue || this.firstValue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -37,7 +37,7 @@ describe('Slider', () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, 100);
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show tooltip', () => {
|
it('show tooltip', () => {
|
||||||
|
@ -55,7 +55,7 @@ describe('Slider', () => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
const slider = vm.$children[0];
|
const slider = vm.$children[0].$children[0];
|
||||||
slider.handleMouseEnter();
|
slider.handleMouseEnter();
|
||||||
expect(slider.$refs.tooltip.showPopper).to.true;
|
expect(slider.$refs.tooltip.showPopper).to.true;
|
||||||
slider.handleMouseLeave();
|
slider.handleMouseLeave();
|
||||||
|
@ -76,14 +76,14 @@ describe('Slider', () => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
const slider = vm.$children[0];
|
const slider = vm.$children[0].$children[0];
|
||||||
|
slider.onButtonDown({ clientX: 0 });
|
||||||
|
slider.onDragging({ clientX: 100 });
|
||||||
|
slider.onDragEnd();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
slider.onButtonDown({ clientX: 0 });
|
|
||||||
slider.onDragging({ clientX: 100 });
|
|
||||||
slider.onDragEnd();
|
|
||||||
expect(vm.value > 0).to.true;
|
expect(vm.value > 0).to.true;
|
||||||
done();
|
done();
|
||||||
}, 150);
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('step', done => {
|
it('step', done => {
|
||||||
|
@ -100,14 +100,14 @@ describe('Slider', () => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
const slider = vm.$children[0];
|
const slider = vm.$children[0].$children[0];
|
||||||
|
slider.onButtonDown({ clientX: 0 });
|
||||||
|
slider.onDragging({ clientX: 100 });
|
||||||
|
slider.onDragEnd();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
slider.onButtonDown({ clientX: 0 });
|
|
||||||
slider.onDragging({ clientX: 100 });
|
|
||||||
slider.onDragEnd();
|
|
||||||
expect(vm.value > 0.4 && vm.value < 0.6).to.true;
|
expect(vm.value > 0.4 && vm.value < 0.6).to.true;
|
||||||
done();
|
done();
|
||||||
}, 150);
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('click', done => {
|
it('click', done => {
|
||||||
|
@ -130,8 +130,8 @@ describe('Slider', () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(vm.value > 0).to.true;
|
expect(vm.value > 0).to.true;
|
||||||
done();
|
done();
|
||||||
}, 150);
|
}, 10);
|
||||||
}, 150);
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('disabled', done => {
|
it('disabled', done => {
|
||||||
|
@ -148,15 +148,14 @@ describe('Slider', () => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
const slider = vm.$children[0];
|
const slider = vm.$children[0].$children[0];
|
||||||
|
slider.onButtonDown({ clientX: 0 });
|
||||||
|
slider.onDragging({ clientX: 100 });
|
||||||
|
slider.onDragEnd();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
slider.onButtonDown({ clientX: 0 });
|
|
||||||
slider.onDragging({ clientX: 100 });
|
|
||||||
slider.onDragEnd();
|
|
||||||
slider.onSliderClick({ clientX: 200 });
|
|
||||||
expect(vm.value).to.equal(0);
|
expect(vm.value).to.equal(0);
|
||||||
done();
|
done();
|
||||||
}, 100);
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show input', done => {
|
it('show input', done => {
|
||||||
|
@ -180,17 +179,145 @@ describe('Slider', () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(vm.value).to.equal(40);
|
expect(vm.value).to.equal(40);
|
||||||
done();
|
done();
|
||||||
}, 150);
|
}, 10);
|
||||||
}, 150);
|
}, 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('show stops', done => {
|
it('show stops', () => {
|
||||||
vm = createTest(Slider, {
|
vm = createTest(Slider, {
|
||||||
showStops: true,
|
showStops: true,
|
||||||
step: 10
|
step: 10
|
||||||
}, true);
|
}, true);
|
||||||
const stops = vm.$el.querySelectorAll('.el-slider__stop');
|
const stops = vm.$el.querySelectorAll('.el-slider__stop');
|
||||||
expect(stops.length).to.equal(9);
|
expect(stops.length).to.equal(9);
|
||||||
done();
|
});
|
||||||
|
|
||||||
|
describe('range', () => {
|
||||||
|
it('basic ranged slider', () => {
|
||||||
|
vm = createVue({
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<el-slider v-model="value" range></el-slider>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: [10, 20]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
const buttons = vm.$children[0].$children;
|
||||||
|
expect(buttons.length).to.equal(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not exceed min and max', done => {
|
||||||
|
vm = createVue({
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<el-slider v-model="value" range :min="50">
|
||||||
|
</el-slider>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: [50, 60]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
setTimeout(() => {
|
||||||
|
vm.value = [40, 60];
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(vm.value).to.deep.equal([50, 60]);
|
||||||
|
vm.value = [50, 120];
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(vm.value).to.deep.equal([50, 100]);
|
||||||
|
done();
|
||||||
|
}, 10);
|
||||||
|
}, 10);
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('click', done => {
|
||||||
|
vm = createVue({
|
||||||
|
template: `
|
||||||
|
<div style="width: 200px;">
|
||||||
|
<el-slider range v-model="value"></el-slider>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: [0, 100]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
const slider = vm.$children[0];
|
||||||
|
setTimeout(() => {
|
||||||
|
slider.onSliderClick({ clientX: 100 });
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(vm.value[0] > 0).to.true;
|
||||||
|
expect(vm.value[1]).to.equal(100);
|
||||||
|
done();
|
||||||
|
}, 10);
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('responsive to dynamic min and max', done => {
|
||||||
|
vm = createVue({
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<el-slider v-model="value" range :min="min" :max="max">
|
||||||
|
</el-slider>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
value: [50, 80]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
setTimeout(() => {
|
||||||
|
vm.min = 60;
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(vm.value).to.deep.equal([60, 80]);
|
||||||
|
vm.min = 30;
|
||||||
|
vm.max = 40;
|
||||||
|
setTimeout(() => {
|
||||||
|
expect(vm.value).to.deep.equal([40, 40]);
|
||||||
|
done();
|
||||||
|
}, 10);
|
||||||
|
}, 10);
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('show stops', done => {
|
||||||
|
vm = createVue({
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<el-slider
|
||||||
|
v-model="value"
|
||||||
|
range
|
||||||
|
:step="10"
|
||||||
|
show-stops></el-slider>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: [30, 60]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
setTimeout(() => {
|
||||||
|
const stops = vm.$el.querySelectorAll('.el-slider__stop');
|
||||||
|
expect(stops.length).to.equal(5);
|
||||||
|
done();
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue