输入类

滑动选择器

滑动选择器,使用拖动交互快速选择数值或数值范围,与 InputNumber 相比更直观

代码演示

如何引入

<script setup lang="ts">
    import { Slider } from '@fibrous/semi-ui-vue';
</script>

基本用法

基本滑动条。当 range 为 true 时,支持两侧滑动。当 disabled 为 true 时,滑块处于不可用状态。

默认
0100


范围
0100


禁用
0100
代码
<script setup lang="ts">
    import { Slider } from '@fibrous/semi-ui-vue';
</script>

<template>
    <div>
        <div>
            <div>默认</div>
            <Slider aria-label="默认滑动输入条" showBoundary />
        </div>
        <br />
        <br />
        <div>
            <div>范围</div>
            <Slider aria-label="范围滑动输入条" :defaultValue="[20, 60]" range />
        </div>
        <br />
        <br />
        <div>
            <div>禁用</div>
            <Slider aria-label="禁用滑动输入条" :defaultValue="40" disabled />
        </div>
    </div>
</template>

带输入框的

滑动条的滑块和输入框组件保持同步。

0100
代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { InputNumber, Slider } from '@fibrous/semi-ui-vue';

    const value = ref(10);

    const getSliderValue = (newValue: number | string | number[]) => {
        if (isNaN(Number(newValue))) {
            return;
        }
        value.value = Number(newValue);
    };
</script>

<template>
    <div>
        <div :style="{ width: '320px', marginRight: '15px' }">
            <Slider aria-label="数值" :modelValue="value" :step="1" @change="getSliderValue" />
        </div>
        <InputNumber aria-label="数值" :max="100" :min="0" :modelValue="value" :style="{ width: '100px' }" @change="getSliderValue" />
    </div>
</template>

自定义提示

使用 tipFormatter 可以设置 Tooltip 的显示格式,传入 null 则隐藏 Tooltip。getAriaValueText 用于给滑块的当前值提供一个用户友好的名称,对屏幕阅读器用户很重要。

提示需要节点内容时使用 #tip 插槽,作用域参数 value 为 tipFormatter 的结果。

0100


0100
代码
<script setup lang="ts">
    import { Slider } from '@fibrous/semi-ui-vue';

    const formatPercent = (value: number) => `${value}%`;
</script>

<template>
    <div>
        <Slider aria-label="带提示的滑块" :getAriaValueText="formatPercent" :tipFormatter="formatPercent" />
        <br />
        <br />
        <Slider aria-label="不带提示的滑块" :tipFormatter="null" />
    </div>
</template>

带标签的

使用 marks 属性标注滑块的刻度,使用 modelValue / defaultValue 指定滑块位置。刻度标签需要节点内容时使用 #mark 插槽。

step=10
01020304050100
0100


step=0.1
0.10.20.30.40.5
01


Marks
20°C40°C
0100


Included
20°C40°C
0100
代码
<script setup lang="ts">
    import { Slider } from '@fibrous/semi-ui-vue';

    const stepTenMarks = { 0: '0', 10: '10', 20: '20', 30: '30', 40: '40', 50: '50', 100: '100' };
    const stepTenthMarks = { 0.1: '0.1', 0.2: '0.2', 0.3: '0.3', 0.4: '0.4', 0.5: '0.5' };
    const celsiusMarks = { 20: '20°C', 40: '40°C' };
    const formatCelsius = (value: number) => `${value}°C`;
</script>

<template>
    <div>
        <div id="slider-marks-step-10">step=10</div>
        <Slider aria-labelledby="slider-marks-step-10" :defaultValue="[10, 100]" :marks="stepTenMarks" range :step="10" />
        <br />
        <br />
        <div id="slider-marks-step-01">step=0.1</div>
        <Slider aria-labelledby="slider-marks-step-01" :defaultValue="[0.1, 0.5]" :marks="stepTenthMarks" :max="1" :min="0" range :step="0.1" />
        <br />
        <br />
        <div id="slider-marks-marks">Marks</div>
        <Slider aria-labelledby="slider-marks-marks" :defaultValue="[0, 100]" :getAriaValueText="formatCelsius" :marks="celsiusMarks" range :tipFormatter="formatCelsius" />
        <br />
        <br />
        <div id="slider-marks-included">Included</div>
        <Slider aria-labelledby="slider-marks-included" :defaultValue="[0, 100]" :getAriaValueText="formatCelsius" :included="false" :marks="celsiusMarks" range :tipFormatter="formatCelsius" />
    </div>
</template>

分段背景

通过使用 linear-gradient 及 railStyle ,配合 @change 可以实现动态的分段背景效果。

10100
代码
<script setup lang="ts">
    import { computed, ref } from 'vue';
    import { Slider } from '@fibrous/semi-ui-vue';

    const range = [10, 100];

    const getRailStyle = (value: number[]) => {
        // transparent lets the middle segment show the track color
        const color = ['var(--semi-color-danger)', 'transparent', 'var(--semi-color-success)'];
        const gradientPos = value.map((val) => Number(((val - range[0]) / (range[1] - range[0])).toFixed(2)) * 100);
        return {
            background: `linear-gradient(to right, ${color[0]} ${gradientPos[0]}%, ${color[1]} ${gradientPos[0]}%, ${color[1]} ${gradientPos[1]}%, ${color[2]} ${gradientPos[1]}%)`,
        };
    };

    const value = ref([20, 60]);

    const changeValue = (newValue: number | number[]) => {
        if (Array.isArray(newValue)) {
            value.value = newValue;
        }
    };

    const railStyle = computed(() => getRailStyle(value.value));
</script>

<template>
    <Slider aria-label="Value range" :max="range[1]" :min="range[0]" :modelValue="value" :railStyle="railStyle" range @change="changeValue" />
</template>

受控组件

滑块位置即 Slider 的值由 modelValue 控制,配合 v-model 使用。只传 :modelValue 而不回写时,拖动、点击与方向键仅触发 change 通知,滑块不移动。



0100
代码
<script setup lang="ts">
    import { ref } from 'vue';
    import { Button, Slider } from '@fibrous/semi-ui-vue';

    const value = ref(10);

    const changeValue = () => {
        value.value += 10;
    };
</script>

<template>
    <div>
        <Button :style="{ marginRight: '20px' }" @click="changeValue">点击改变value值</Button>
        <br />
        <br />
        <Slider aria-label="受控滑块" :modelValue="value" />
    </div>
</template>

垂直

0100
0100
0100
0100
20°C40°C
0100
20°C40°C
0100
代码
<script setup lang="ts">
    import { Slider } from '@fibrous/semi-ui-vue';

    const boxStyle = {
        height: '300px',
        marginLeft: '30px',
        marginTop: '10px',
        paddingRight: '30px',
        display: 'inline-block',
    };
    const marks = { 20: '20°C', 40: '40°C' };
</script>

<template>
    <div>
        <div :style="boxStyle">
            <Slider aria-label="Vertical slider" vertical />
        </div>
        <div :style="boxStyle">
            <Slider aria-label="Reversed vertical slider" vertical verticalReverse />
        </div>
        <div :style="boxStyle">
            <Slider aria-label="Vertical range slider" :defaultValue="[20, 60]" range vertical />
        </div>
        <div :style="boxStyle">
            <Slider aria-label="Reversed vertical range slider" :defaultValue="[20, 60]" range vertical verticalReverse />
        </div>
        <div :style="boxStyle">
            <Slider aria-label="Vertical range slider with marks" :defaultValue="[20, 60]" :marks="marks" range :step="10" vertical />
        </div>
        <div :style="boxStyle">
            <Slider aria-label="Reversed vertical range slider with marks" :defaultValue="[20, 60]" :marks="marks" range :step="10" vertical verticalReverse />
        </div>
    </div>
</template>

滑块带圆点

Default
0100


Range
0100
代码
<script setup lang="ts">
    import { Slider } from '@fibrous/semi-ui-vue';
</script>

<template>
    <div>
        <div>
            <div id="slider-handle-dot-default">Default</div>
            <Slider aria-labelledby="slider-handle-dot-default" :handleDot="{ size: '4px', color: 'blue' }" showBoundary />
        </div>
        <br />
        <br />
        <div>
            <div id="slider-handle-dot-range">Range</div>
            <Slider
                aria-labelledby="slider-handle-dot-range"
                :defaultValue="[20, 60]"
                :handleDot="[
                    { size: '4px', color: 'blue' },
                    { size: '4px', color: 'pink' },
                ]"
                range
            />
        </div>
    </div>
</template>

API参考

Props

属性说明类型默认值
modelValue当前取值,配合 v-model 使用;只传不回写时拖动、点击与方向键仅触发通知,滑块不移动。变回 undefined 时回到 defaultValue,没有则回到 min,且不触发 afterChange。number | number[]undefined
defaultValue非受控时的初始取值。number | number[]semiGlobalDefault("Slider", "defaultValue")
disabled滑块是否禁用。booleansemiDefault("Slider", "disabled", false)
includedmarks 不为空对象时有效,true 表示刻度为包含关系,false 表示并列。booleansemiDefault("Slider", "included", true)
marks刻度,key 为 number 且取值在闭区间 [min, max] 内,值为字符串或数字;节点内容使用 mark 插槽。TypeRecord<number, string | number>semiGlobalDefault("Slider", "marks")
max最大值。numbersemiDefault("Slider", "max", 100)
min最小值。numbersemiDefault("Slider", "min", 0)
range是否支持两端同时可滑动。booleansemiDefault("Slider", "range", false)
step步长,从 min 起计算。numbersemiDefault("Slider", "step", 1)
tipFormatter设置提示的展示格式,每个手柄单独调用,参数为该手柄的值;默认显示当前取值,null 隐藏提示。Type((value: number) => string | number) | nullundefined
tooltipVisible是否始终显示提示。booleansemiGlobalDefault("Slider", "tooltipVisible")
tooltipOnMark刻度上是否带有提示。booleansemiDefault("Slider", "tooltipOnMark", false)
showMarkLabel是否显示刻度标签。booleansemiDefault("Slider", "showMarkLabel", true)
showArrow提示是否带箭头。booleansemiDefault("Slider", "showArrow", true)
showBoundary是否在悬停时展示最大值与最小值。booleansemiDefault("Slider", "showBoundary", false)
vertical是否为垂直方向。booleansemiDefault("Slider", "vertical", false)
verticalReverse反转垂直方向,即上大下小。booleansemiDefault("Slider", "verticalReverse", false)
railStyle滑块轨道的样式。CSSPropertiessemiGlobalDefault("Slider", "railStyle")
handleDot滑块上的圆点,单个滑块传一个对象,range 时按手柄传数组。HandleDot | HandleDot[]semiGlobalDefault("Slider", "handleDot")
id落在第一个手柄的 input 上,用于 label for 关联,点击 label 会聚焦该手柄。stringundefined
ariaLabel手柄的可访问名称;range 模式下两个手柄在其后分别追加语言包中的起点、终点。stringsemiGlobalDefault("Slider", "ariaLabel")
ariaLabelledby引用标签元素的 id,作为手柄的可访问名称;range 模式下每个手柄再加上起点或终点。stringsemiGlobalDefault("Slider", "ariaLabelledby")
ariaValuetext为当前值提供用户友好的名称。stringsemiGlobalDefault("Slider", "ariaValuetext")
getAriaValueText为当前值提供用户友好的名称,参数为滑块的值与滑块的顺序。Type((value: number, index?: number | undefined) => string)undefined

Events

事件说明回调参数
update:modelValue值变化时触发,供 v-model 回写。[value: number | number[]]
change值变化时触发,参数为当前取值。[value: number | number[]]
afterChange值变化完成后触发,参数为当前取值;受控值被改回 undefined 时不触发。[value: number | number[]]
mouseUp拖动滑块的指针(鼠标、触摸、触控笔)松开或被取消时,以及取值键松开时触发。[event: KeyboardEvent | PointerEvent]
focus任一手柄获得焦点时触发。[event: FocusEvent]
blur焦点离开整个 Slider 时触发,在两个手柄之间切换不触发。[event: FocusEvent]

Slots

插槽说明作用域参数
mark自定义刻度标签,也用于 tooltipOnMark 的提示;作用域参数为刻度的 value 与 label。SliderMarkSlotProps
tip自定义手柄提示的内容;value 为 tipFormatter 的结果,handle 为 min 或 max。SliderTipSlotProps

Exposes

名称说明类型
无

class、style 与其余原生属性经 attrs 落在 .semi-slider-wrapper 上。aria-describedby、aria-invalid、aria-errormessage、name 与 @keydown 落在每个手柄的原生 input 上;tabindex、aria-required 不输出。name 在 range 模式下两个 input 同名,依次提交两个值。

组件模板 ref 的 $el 在水平模式下是外层 .semi-slider 元素,垂直模式下是 .semi-slider-wrapper。

RTL/LTR

  • Slider 的水平方向支持 RTL,可通过 ConfigProvider 配置 direction="rtl" 启用。
  • RTL 模式下,Slider 水平方向的最小值在右侧,最大值在左侧,滑块拖动方向与 LTR 相反。
  • 垂直方向(vertical)不受 RTL 影响。
  • RTL 模式下,键盘方向键的行为会镜像:左箭头 增加值,右箭头 减少值。

Accessibility

ARIA

  • 每个滑块内有一个视觉隐藏的原生 <input type="range">,它是真正的焦点和表单控件(隐式 role 为 slider),可见的滑块只负责绘制和拖动。查询控件时请使用 getByRole('slider') 或 input[type="range"],不要查询 .semi-slider-handle。
  • 当前值、最小值、最大值由 input 的 value、min、max 提供给辅助技术。range 模式下两个 input 的 min、max 都是整个滑条的范围,一个滑块移动到另一个滑块处即停止。
  • 当 Slider 为纵向时,input 带 aria-orientation="vertical",并以 writing-mode: vertical-lr 呈现纵向。
  • 当值不容易被理解时,支持通过 API aria-valuetext 传递一个字符串使其更友好。也可以通过 API getAriaValueText(value, index) 方法得到 aria-valuetext 的值。
  • 支持通过 API aria-label 或者 aria-labelledby 确定 slider 的标签,也可以用 <label for> 指向 id:id 落在第一个滑块的 input 上,点击 label 会聚焦该滑块。
  • 禁用时(disabled)不会用语言包文案覆盖手柄的名称:禁用状态由原生 disabled 表达,aria-label、aria-labelledby 与 <label for> 给出的名称保持不变;没有任何命名时,禁用的手柄和可用的手柄一样没有名称。
  • range 模式下两个滑块各有自己的名称:aria-label 之后追加语言包中的「起点」「终点」(如 价格 起点、价格 终点);使用 aria-labelledby 时,每个滑块的名称是该标签加上「起点」或「终点」;都没有传时名称就是「起点」「终点」。外层 wrapper 没有角色,不再带 aria-label。
  • 在 Form 中使用时,Form 注入的 id、aria-labelledby、aria-describedby、aria-invalid、aria-errormessage 落在 input 上;aria-required 不输出,因为 slider 角色不支持该属性。
  • 传入 name 后,Slider 可以随原生 <form> 提交:单值提交一个值,range 模式下两个 input 使用同一个 name,依次提交两个值(FormData.getAll(name) 得到 [最小值, 最大值])。禁用时与其他原生控件一样不参与提交。

键盘和焦点

  • 方向键由组件处理,各浏览器行为一致;Home、End、Page Up、Page Down 由原生 range 处理。滑块可获取焦点,键盘聚焦时在可见滑块上绘制聚焦轮廓,并展示当前滑块的提示信息;用指针按下滑块时焦点同样落到该滑块,但不显示聚焦轮廓。
  • onKeyDown(Vue 为 onKeydown)绑定在手柄的 input 上,每个按键都会先调用它再由滑块处理;取值键(方向键、Home、End、Page Up、Page Down)默认不冒泡到祖先元素,在回调中调用 preventDefault 可以拦截该键,滑块不再处理它。
  • 当用户使用 range 属性时,可以使用 Tab 及 Shift + Tab 切换左右两个滑块的焦点。
  • 方向键与滑块在屏幕上的移动方向一致:水平时 右箭头、上箭头 增加值,左箭头、下箭头 减少值(RTL 下左右镜像);纵向时值从上往下增大,下箭头 增加值、上箭头 减少值;verticalReverse 时值从下往上增大,上箭头 增加值、下箭头 减少值;纵向时 右箭头 始终增加值,左箭头 始终减少值。
  • 若想要滑块高于步长的变化量,Page Up、Page Down 每次变化整个范围的 10%(按步长取整):
    • Windows 用户: Page Up 用于增加,Page Down 用于减少;
    • Mac 用户使用: Fn + 上箭头 用于增加,Fn + 下箭头 用于减少;
    • 当用户使用 range 属性时,前一个滑块的 Page Up(Fn + 上箭头) 键仅支持到与后一个滑块相遇,重合后再对前一个滑块使用 Page Up 键则无响应。后一个滑块同理,相遇后,对Page Down(Fn + 下箭头) 键无响应。
  • 若想将滑块移动到滑杆的最小值处:
    • Windows 用户: Home ;
    • Mac 用户: Fn + 左箭头;
    • 当用户使用 range 属性时,后一个滑块的 Home(Fn + 左箭头) 键仅支持到与前一个滑块相遇,重合后再次使用 Home(Fn + 左箭头) 键无响应。
  • 若想将滑块移动到滑杆的最大值处:
    • Windows 用户:End ;
    • Mac 用户:Fn + 右箭头;
    • 当用户使用 range 属性时,前一个滑块的 End(Fn + 右箭头) 键仅支持到与后一个滑块相遇,重合后再次使用 End(Fn + 右箭头) 键无响应。

设计变量

聚焦样式说明

Slider 组件有两个与聚焦相关的设计变量,分别对应不同的交互场景:

变量名CSS 变量触发场景说明
圆形按钮轮廓 - 聚焦--semi-color-primary-light-active键盘导航(Tab 键聚焦)滑块内的 input 匹配 :focus-visible 时(.semi-slider-handle:has(> input:focus-visible))在可见滑块上显示 outline,仅在键盘聚焦时显示
滑动条圆形描边颜色 - 激活态--semi-color-focus-border鼠标点击/拖动使用 .semi-slider-handle-clicked 类,在鼠标交互时显示 border

如果你希望在主题配置中修改圆形按钮的聚焦样式,请注意上述两个变量对应不同的交互场景。

下表是组件内部的 Sass 变量,不能覆写。定制样式请覆写 --semi-* CSS 变量(见 Design Token 页),或写不分层的 CSS 规则。
Sass 变量值用法
$transition_duration-slider_handle-bgvar(--semi-transition_duration-none)滑动条圆形按钮-背景色-动画持续时间
$transition_function-slider_handle-bgvar(--semi-transition_function-easeIn)滑动条圆形按钮-背景色-过渡曲线
$transition_delay-slider_handle-bgvar(--semi-transition_delay-none)滑动条圆形按钮-背景色-延迟时间
$transform_scale-slider_handlevar(--semi-transform_scale-small)滑动条圆形按钮-放大
$color-slider-text-defaultvar(--semi-color-text-0)滑动条文本颜色 - 默认
$color-slider_dot-bg-defaultvar(--semi-color-white)滑动条圆形刻度点颜色 - 默认态
$color-slider_dot-border-activevar(--semi-color-white)滑动条圆形刻度点描边颜色 - 按下态
$color-slider_dot-bordervar(--semi-color-white)滑动条圆形刻度点描边颜色 - 默认态
$color-slider_handle-bg-defaultvar(--semi-color-white)滑动条圆形按钮颜色 - 默认态
$color-slider_handle-bg-hovervar(--semi-color-white)滑动条圆形按钮颜色 - 悬停态
$color-slider_handle-border-focusvar(--semi-color-focus-border)滑动条圆形描边颜色 - 激活态
$color-slider_handle_disabled-border-hovervar(--semi-color-white)禁用滑动条圆形描边颜色 - 悬停态
$color-slider_handle_disabled-bordervar(--semi-color-border)禁用滑动条圆形描边颜色 - 默认态
$color-slider_mark-text-defaultvar(--semi-color-text-2)滑动条刻度文字颜色
$color-slider_rail-bg-defaultvar(--semi-color-fill-0)滑动条轨道颜色 - 未填充
$color-slider_railrgba(0, 0, 0, 0.65)
$color-slider_track-bg-defaultvar(--semi-color-primary)滑动条轨道颜色 - 已填充
$color-slider_track_disabled-bgvar(--semi-color-primary-disabled)禁用滑动条轨道颜色 - 已填充
$color-slider_handle-focus$color-focus-ring圆形按钮轮廓 - 聚焦
$color-slider_handle_dotvar(--semi-color-primary)圆形按钮内部圆点颜色
$spacing-slider-paddingX13px滑动条整体水平内边距
$spacing-slider-paddingY0px滑动条整体垂直内边距
$spacing-slider-margin0px滑动条整体外边距
$spacing-slider_rail-margin0px滑动条轨道外边距
$spacing-slider_rail-padding0px滑动条轨道内边距
$spacing-slider_rail-top14px滑动条未填充轨道顶部距离
$spacing-slider_handle-marginTop4px滑动条圆形按钮顶部外边距
$spacing-slider_track-top14px滑动条已填充轨道顶部距离
$spacing-slider_tooltip-top-40px滑动条工具提示顶部距离
$spacing-slider_dot-top14px滑动条圆形刻度点顶部距离
$spacing-slider_marks-top23px滑动条刻度标签顶部距离
$spacing-slider_marks-left0px滑动条刻度标签左侧距离
$spacing-slider_boundary-top30px
$spacing-slider_boundary_min-left0px
$spacing-slider_boundary_max-right0px
$spacing-slider_vertical_marks-marginTop-30px垂直滑动条刻度标签顶部外边距
$spacing-slider_vertical_marks-marginLeft29px垂直滑动条刻度标签左侧外边距
$spacing-slider_vertical_marks-reverse-marginLeft-26px垂直滑动条刻度标签左侧外边距(标签在左侧时)
$spacing-slider_vertical_rail-top0px垂直滑动条轨道顶部距离
$spacing-slider_vertical_handle-marginTop0px垂直滑动条原型按钮顶部外边距
$spacing-slider_vertical_handle-marginLeft-10px垂直滑动条原型按钮左侧外边距
$spacing-slider_handle-translateY0px水平滑动条原型按钮垂直偏移量
$spacing-slider_vertical_handle-translateX0px垂直滑动条原型按钮水平偏移量
$spacing-slider_dot-translateX0px水平滑动条圆形刻度点水平偏移量
$spacing-slider_vertical_dot-translateY0px垂直滑动条圆形刻度点垂直偏移量
$radius-slider_railvar(--semi-border-radius-small)滚动条未填充轨道圆角
$radius-slider_trackvar(--semi-border-radius-small)滚动条已填充轨道圆角
$height-slider_wrapper32px滚动条容器整体高度
$height-slider_vertical_wrapper4px垂直滚动条整体宽度
$height-slider_rail4px滚动条未填充轨道高度
$width-slider_handle24px滚动条圆形按钮宽度
$width-slider_handle_clicked1px滚动条圆形按钮按下后描边宽度
$height-slider_track4px滚动条已填充轨道高度
$width-slider_dot4px滚动条圆形刻度点宽度
$width-slider_handle_border_disabled1px禁用滚动条圆形按钮按下后描边宽度
$width-slider_handle-focus$width-focus-ring圆形按钮轮廓 - 聚焦
$width-slider_handle_dot4px圆形按钮内部圆点宽度
$font-slider_rail-fontSize14px滚动条轨道文本字号
$font-slider_marks-fontSize14px滚动条刻度标签字号
$font-slider_rail-lineHeight1.5滚动条轨道文本行高倍数