Element UI滑块输入:Slider范围选择组件
【免费下载链接】element A Vue.js 2.0 UI Toolkit for Web 项目地址: https://gitcode.com/gh_mirrors/eleme/element
组件概述
Element UI的Slider(滑块)组件是一个功能丰富的范围选择工具,允许用户通过直观的拖动交互选择数值或数值范围。该组件支持单值选择和范围选择两种模式,提供了丰富的配置选项如刻度标记、输入框显示、垂直布局等,满足不同场景下的数值输入需求。
Slider组件的核心实现位于packages/slider/目录下,主要包括入口文件index.js和组件实现src/main.vue。
基本用法
单值滑块
单值滑块允许用户选择单个数值,默认范围为0-100。基础用法示例:
<template>
<el-slider v-model="value"></el-slider>
</template>
<script>
export default {
data() {
return {
value: 50
};
}
};
</script>
上述代码会渲染一个水平滑块,初始值为50,用户可以通过拖动滑块手柄来改变数值。
范围滑块
范围滑块允许用户选择一个数值区间,通过设置range属性为true启用。示例:
<template>
<el-slider
v-model="value"
range
:min="0"
:max="100"
:step="5">
</el-slider>
</template>
<script>
export default {
data() {
return {
value: [20, 60]
};
}
};
</script>
范围滑块会显示两个可拖动的手柄,分别对应区间的最小值和最大值。绑定的value是一个包含两个元素的数组。
组件结构解析
组件入口
Slider组件的入口文件index.js非常简洁,主要负责组件的注册:
import Slider from './src/main';
/* istanbul ignore next */
Slider.install = function(Vue) {
Vue.component(Slider.name, Slider);
};
export default Slider;
这段代码导入了Slider组件的主文件,并添加了一个install方法,使Slider可以通过Vue.use()方式全局注册。
核心模板结构
Slider组件的模板结构位于src/main.vue,主要包含以下几个部分:
- 主容器div,类名为"el-slider"
- 可选的输入框(当showInput为true时显示)
- 滑道(runway)元素,包含进度条(bar)和滑块按钮(slider-button)
- 可选的刻度标记(stops)和自定义标记(marks)
核心模板代码片段:
<div class="el-slider" :class="{ 'is-vertical': vertical, 'el-slider--with-input': showInput }">
<el-input-number
v-model="firstValue"
v-if="showInput && !range"
class="el-slider__input"
ref="input"
@change="emitChange"
:step="step"
:disabled="sliderDisabled"
:controls="showInputControls"
:min="min"
:max="max"
:debounce="debounce"
:size="inputSize">
</el-input-number>
<div class="el-slider__runway" :style="runwayStyle" @click="onSliderClick" ref="slider">
<div class="el-slider__bar" :style="barStyle"></div>
<slider-button
:vertical="vertical"
v-model="firstValue"
:tooltip-class="tooltipClass"
ref="button1">
</slider-button>
<slider-button
:vertical="vertical"
v-model="secondValue"
:tooltip-class="tooltipClass"
ref="button2"
v-if="range">
</slider-button>
<!-- 刻度和标记部分省略 -->
</div>
</div>
核心功能实现
数据绑定与状态管理
Slider组件使用了多个数据属性来管理状态,主要包括:
data() {
return {
firstValue: null, // 第一个滑块的值(单值模式或范围模式的最小值)
secondValue: null, // 范围模式的最大值
oldValue: null, // 上一次的值,用于比较变化
dragging: false, // 是否正在拖动滑块
sliderSize: 1 // 滑块轨道的尺寸(宽度或高度)
};
}
组件通过watch监听value属性的变化,当外部传入的value改变时,更新内部状态:
watch: {
value(val, oldVal) {
if (this.dragging ||
Array.isArray(val) &&
Array.isArray(oldVal) &&
val.every((item, index) => item === oldVal[index])) {
return;
}
this.setValues();
},
// 其他watch项...
}
滑块位置计算
Slider组件的核心功能是将滑块的位置转换为对应的数值,以及将数值转换为滑块位置。这一功能主要通过以下方法实现:
methods: {
setPosition(percent) {
const targetValue = this.min + percent * (this.max - this.min) / 100;
if (!this.range) {
this.$refs.button1.setPosition(percent);
return;
}
// 范围模式下的位置计算逻辑...
},
onSliderClick(event) {
if (this.sliderDisabled || this.dragging) return;
this.resetSize();
if (this.vertical) {
const sliderOffsetBottom = this.$refs.slider.getBoundingClientRect().bottom;
this.setPosition((sliderOffsetBottom - event.clientY) / this.sliderSize * 100);
} else {
const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
this.setPosition((event.clientX - sliderOffsetLeft) / this.sliderSize * 100);
}
this.emitChange();
},
resetSize() {
if (this.$refs.slider) {
this.sliderSize = this.$refs.slider[`client${ this.vertical ? 'Height' : 'Width' }`];
}
}
// 其他方法...
}
setPosition方法将百分比位置转换为实际数值,onSliderClick方法处理滑块轨道的点击事件,计算点击位置对应的百分比,进而设置滑块位置。
样式计算
Slider组件的进度条样式通过计算属性动态生成:
computed: {
barSize() {
return this.range
? `${ 100 * (this.maxValue - this.minValue) / (this.max - this.min) }%`
: `${ 100 * (this.firstValue - this.min) / (this.max - this.min) }%`;
},
barStart() {
return this.range
? `${ 100 * (this.minValue - this.min) / (this.max - this.min) }%`
: '0%';
},
barStyle() {
return this.vertical
? {
height: this.barSize,
bottom: this.barStart
} : {
width: this.barSize,
left: this.barStart
};
}
// 其他计算属性...
}
barSize计算进度条的长度百分比,barStart计算进度条的起始位置,barStyle则根据水平/垂直方向返回不同的样式对象。
高级特性
刻度与标记
Slider组件支持两种类型的标记:常规刻度和自定义标记。
常规刻度
通过设置showStops属性为true,可以在滑块上显示刻度:
<el-slider
v-model="value"
:step="10"
show-stops>
</el-slider>
刻度的计算逻辑位于stops计算属性中:
stops() {
if (!this.showStops || this.min > this.max) return [];
if (this.step === 0) {
process.env.NODE_ENV !== 'production' &&
console.warn('[Element Warn][Slider]step should not be 0.');
return [];
}
const stopCount = (this.max - this.min) / this.step;
const stepWidth = 100 * this.step / (this.max - this.min);
const result = [];
for (let i = 1; i < stopCount; i++) {
result.push(i * stepWidth);
}
// 根据当前值过滤需要显示的刻度...
return result;
}
自定义标记
通过marks属性可以设置自定义标记,支持在特定值位置显示文本:
<el-slider
v-model="value"
:marks="marks">
</el-slider>
<script>
export default {
data() {
return {
value: 30,
marks: {
0: '0°C',
50: '50°C',
100: '100°C'
}
};
}
};
</script>
自定义标记的处理逻辑位于markList计算属性:
markList() {
if (!this.marks) {
return [];
}
const marksKeys = Object.keys(this.marks);
return marksKeys.map(parseFloat)
.sort((a, b) => a - b)
.filter(point => point <= this.max && point >= this.min)
.map(point => ({
point,
position: (point - this.min) * 100 / (this.max - this.min),
mark: this.marks[point]
}));
}
垂直滑块
通过设置vertical属性为true,可以将滑块垂直显示:
<el-slider
v-model="value"
vertical
height="200px">
</el-slider>
垂直滑块的样式和交互逻辑与水平滑块有所不同,主要通过CSS类和条件渲染实现:
// 计算滑块尺寸
resetSize() {
if (this.$refs.slider) {
this.sliderSize = this.$refs.slider[`client${ this.vertical ? 'Height' : 'Width' }`];
}
},
// 处理点击事件
onSliderClick(event) {
if (this.sliderDisabled || this.dragging) return;
this.resetSize();
if (this.vertical) {
const sliderOffsetBottom = this.$refs.slider.getBoundingClientRect().bottom;
this.setPosition((sliderOffsetBottom - event.clientY) / this.sliderSize * 100);
} else {
const sliderOffsetLeft = this.$refs.slider.getBoundingClientRect().left;
this.setPosition((event.clientX - sliderOffsetLeft) / this.sliderSize * 100);
}
this.emitChange();
}
输入框显示
通过设置showInput属性为true,可以在滑块旁边显示一个数字输入框,允许用户直接输入数值:
<el-slider
v-model="value"
show-input>
</el-slider>
输入框的实现使用了Element UI的InputNumber组件,相关代码位于模板中的el-input-number元素:
<el-input-number
v-model="firstValue"
v-if="showInput && !range"
class="el-slider__input"
ref="input"
@change="emitChange"
:step="step"
:disabled="sliderDisabled"
:controls="showInputControls"
:min="min"
:max="max"
:debounce="debounce"
:size="inputSize">
</el-input-number>
组件API
Props
Slider组件提供了丰富的属性配置,以下是主要的Props:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| value | Number / Array | 0 | 绑定值,单值或范围数组 |
| min | Number | 0 | 最小值 |
| max | Number | 100 | 最大值 |
| step | Number | 1 | 步长,取值必须大于0,并且可被(max - min)整除 |
| range | Boolean | false | 是否为范围选择 |
| vertical | Boolean | false | 是否垂直显示 |
| showInput | Boolean | false | 是否显示输入框 |
| showInputControls | Boolean | true | 是否显示输入框的控制按钮 |
| showStops | Boolean | false | 是否显示间断点 |
| showTooltip | Boolean | true | 是否显示 tooltip |
| formatTooltip | Function | - | 格式化 tooltip message |
| disabled | Boolean | false | 是否禁用 |
| marks | Object | - | 标记,键为数值,值为显示的文本 |
| height | String | - | 垂直模式下的高度 |
Events
| 事件名称 | 说明 | 回调参数 |
|---|---|---|
| input | 实时值变化时触发 | 改变后的值 |
| change | 当用户点击滑块/输入框确认,或拖动滑块结束时触发 | 改变后的值 |
Methods
| 方法名 | 说明 | 参数 |
|---|---|---|
| resetSize | 重置滑块尺寸 | - |
| setPosition | 设置滑块位置 | 百分比位置 |
实际应用场景
音量控制
<template>
<div class="volume-control">
<el-icon name="volume"></el-icon>
<el-slider
v-model="volume"
:min="0"
:max="100"
:step="1"
:show-tooltip="false"
class="volume-slider">
</el-slider>
<el-icon name="volume-full"></el-icon>
</div>
</template>
<script>
export default {
data() {
return {
volume: 70
};
}
};
</script>
<style scoped>
.volume-control {
display: flex;
align-items: center;
padding: 10px;
}
.volume-slider {
width: 150px;
margin: 0 10px;
}
</style>
价格范围筛选
<template>
<div class="price-filter">
<el-slider
v-model="priceRange"
range
:min="0"
:max="1000"
:step="10"
:show-stops="true"
:marks="priceMarks">
</el-slider>
<div class="price-display">
价格范围: ¥{{ priceRange[0] }} - ¥{{ priceRange[1] }}
</div>
</div>
</template>
<script>
export default {
data() {
return {
priceRange: [200, 800],
priceMarks: {
0: '¥0',
500: '¥500',
1000: '¥1000+'
}
};
}
};
</script>
颜色透明度调节
<template>
<div class="color-opacity-control">
<div
class="color-preview"
:style="{ backgroundColor: `rgba(255, 0, 0, ${opacity / 100})` }">
</div>
<el-slider
v-model="opacity"
:min="0"
:max="100"
:step="1"
show-input>
</el-slider>
</div>
</template>
<script>
export default {
data() {
return {
opacity: 70
};
}
};
</script>
<style scoped>
.color-preview {
width: 50px;
height: 50px;
border-radius: 4px;
margin-bottom: 10px;
}
</style>
组件源码解析
滑块按钮组件
Slider组件依赖于一个内部的滑块按钮组件,位于src/button.vue。这个组件负责处理滑块的拖动逻辑和tooltip显示。
核心代码片段:
<template>
<div
class="el-slider-button-wrapper"
:class="{ 'hover': hover, 'dragging': dragging }"
@mouseenter="hover = true"
@mouseleave="hover = false"
@mousedown="startDrag"
@touchstart.prevent="startDrag">
<div class="el-slider-button" :style="buttonStyle"></div>
<el-tooltip
v-if="showTooltip || dragging"
:class="tooltipClass"
:placement="vertical ? 'right' : 'top'"
:content="formatTooltip ? formatTooltip(currentValue) : currentValue"
:disabled="!showTooltip && !dragging">
</el-tooltip>
</div>
</template>
滑块按钮组件处理了鼠标/触摸事件,实现了拖动功能,并根据配置显示tooltip。
拖动逻辑实现
拖动功能的核心实现位于滑块按钮组件的mousedown/touchstart事件处理中:
methods: {
startDrag(event) {
// 初始化拖动状态
this.dragging = true;
this.hover = true;
// 记录初始位置和值
const clientX = event.type === 'touchstart' ? event.touches[0].clientX : event.clientX;
const clientY = event.type === 'touchstart' ? event.touches[0].clientY : event.clientY;
this初始位置 = { clientX, clientY };
this初始值 = this.currentValue;
// 绑定mousemove和mouseup事件
document.addEventListener('mousemove', this.onDrag);
document.addEventListener('touchmove', this.onDrag);
document.addEventListener('mouseup', this.stopDrag);
document.addEventListener('touchend', this.stopDrag);
document.addEventListener('contextmenu', this.stopDrag);
},
onDrag(event) {
if (!this.dragging) return;
// 计算拖动距离
const clientX = event.type === 'touchmove' ? event.touches[0].clientX : event.clientX;
const clientY = event.type === 'touchmove' ? event.touches[0].clientY : event.clientY;
const delta = this.vertical
? this初始位置.clientY - clientY
: clientX - this初始位置.clientX;
const percent = delta / this.sliderSize * 100;
const stepValue = (this.max - this.min) * percent / 100;
let value = this.初始值 + stepValue;
// 处理边界值和步长
value = this.formatValue(value);
// 更新值
this.$emit('input', value);
this.$parent.$emit('input', this.range ? this.$parent.value : value);
},
stopDrag() {
if (this.dragging) {
this.dragging = false;
this.$parent.resetSize();
this.$parent.emitChange();
}
// 移除事件监听
document.removeEventListener('mousemove', this.onDrag);
document.removeEventListener('touchmove', this.onDrag);
document.removeEventListener('mouseup', this.stopDrag);
document.removeEventListener('touchend', this.stopDrag);
document.removeEventListener('contextmenu', this.stopDrag);
}
}
这段代码实现了滑块拖动的完整逻辑,包括开始拖动、拖动中更新值、结束拖动三个阶段,处理了鼠标和触摸两种输入方式。
总结
Element UI的Slider组件是一个功能丰富、高度可定制的范围选择工具。它支持单值和范围两种选择模式,提供了多种自定义选项如刻度、标记、垂直布局等。通过深入分析其源码,我们可以看到组件的设计思路和实现细节,包括:
- 组件的基本结构和入口设计
- 数据绑定和状态管理
- 滑块位置计算和样式动态生成
- 拖动交互的实现
- 各种高级特性的支持
Slider组件的源码位于packages/slider/目录下,主要包括:
- index.js:组件入口文件
- src/main.vue:主组件实现
- src/button.vue:滑块按钮组件
- src/marker.vue:标记组件
通过灵活配置Slider组件的各种属性,开发者可以轻松实现各种数值选择功能,提升用户体验。无论是简单的音量控制,还是复杂的价格范围筛选,Slider组件都能满足需求。
扩展阅读
- Element UI官方文档:examples/docs/zh-CN/slider.md
- Slider组件测试用例:test/unit/specs/slider.spec.js
- 组件样式定义:packages/theme-chalk/src/slider.scss
【免费下载链接】element A Vue.js 2.0 UI Toolkit for Web 项目地址: https://gitcode.com/gh_mirrors/eleme/element
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



