Element UI滑块输入:Slider范围选择组件

Element UI滑块输入:Slider范围选择组件

【免费下载链接】element A Vue.js 2.0 UI Toolkit for Web 【免费下载链接】element 项目地址: 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,主要包含以下几个部分:

  1. 主容器div,类名为"el-slider"
  2. 可选的输入框(当showInput为true时显示)
  3. 滑道(runway)元素,包含进度条(bar)和滑块按钮(slider-button)
  4. 可选的刻度标记(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:

参数类型默认值说明
valueNumber / Array0绑定值,单值或范围数组
minNumber0最小值
maxNumber100最大值
stepNumber1步长,取值必须大于0,并且可被(max - min)整除
rangeBooleanfalse是否为范围选择
verticalBooleanfalse是否垂直显示
showInputBooleanfalse是否显示输入框
showInputControlsBooleantrue是否显示输入框的控制按钮
showStopsBooleanfalse是否显示间断点
showTooltipBooleantrue是否显示 tooltip
formatTooltipFunction-格式化 tooltip message
disabledBooleanfalse是否禁用
marksObject-标记,键为数值,值为显示的文本
heightString-垂直模式下的高度

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组件是一个功能丰富、高度可定制的范围选择工具。它支持单值和范围两种选择模式,提供了多种自定义选项如刻度、标记、垂直布局等。通过深入分析其源码,我们可以看到组件的设计思路和实现细节,包括:

  1. 组件的基本结构和入口设计
  2. 数据绑定和状态管理
  3. 滑块位置计算和样式动态生成
  4. 拖动交互的实现
  5. 各种高级特性的支持

Slider组件的源码位于packages/slider/目录下,主要包括:

通过灵活配置Slider组件的各种属性,开发者可以轻松实现各种数值选择功能,提升用户体验。无论是简单的音量控制,还是复杂的价格范围筛选,Slider组件都能满足需求。

扩展阅读

【免费下载链接】element A Vue.js 2.0 UI Toolkit for Web 【免费下载链接】element 项目地址: https://gitcode.com/gh_mirrors/eleme/element

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值