UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

309 lines (265 loc) 18.5 kB
--- localeCode: zh-CN order: 47 category: 输入类 title: Slider 滑动选择器 icon: doc-slider brief: 滑动选择器,使用拖动交互快速选择数值或数值范围,与 InputNumber 相比更直观 --- ## 代码演示 ### 如何引入 ```jsx import import { Slider } from '@douyinfe/semi-ui'; ``` ### 基本用法 基本滑动条。当 `range` 为 `true` 时,支持两侧滑动。当 `disabled` 为 `true` 时,滑块处于不可用状态。 ```jsx live=true import React from 'react'; import { Slider } from '@douyinfe/semi-ui'; () => ( <div> <div> <div>Default</div> <Slider showBoundary={true}></Slider> </div> <br/> <br/> <div> <div>Range</div> <Slider defaultValue={[20, 60]} range></Slider> </div> <br/> <br/> <div> <div>Disabled</div> <Slider defaultValue={40} disabled></Slider> </div> </div> ); ``` ### 带输入框的 滑动条的滑块和输入框组件保持同步。 ```jsx live=true import React, { useState } from 'react'; import { Slider, InputNumber } from '@douyinfe/semi-ui'; () => { const [value, setValue] = useState(10); const getSliderValue = (newValue) => { if (isNaN(Number(newValue))){ return; } setValue(newValue / 1); }; return ( <div> <div style={{ width: 320, marginRight: 15 }}> <Slider step={1} value={value} onChange={getSliderValue} ></Slider> </div> <InputNumber onChange={getSliderValue} style={{ width: 100 }} value={value} min={0} max={100} /> </div> ); }; ``` ### 自定义提示 使用 `tipFormatter` 可以设置 Tooltip 的显示的格式。设置 `tipFormatter={null}`,则隐藏 Tooltip。`getAriaValueText`用于给滑块的当前值提供一个用户友好的名称,对屏幕阅读器用户很重要。 ```jsx live=true import React from 'react'; import { Slider } from '@douyinfe/semi-ui'; () => ( <div> <Slider tipFormatter={v => (`${v}%`)} getAriaValueText={v => (`${v}%`)}/> <br/> <br/> <Slider tipFormatter={null} /> </div> ); ``` ### 带标签的 使用 `marks` 属性标注滑块的刻度,使用 `value` / `defaultValue` 指定滑块位置。 ```jsx live=true import React from 'react'; import { Slider } from '@douyinfe/semi-ui'; () => ( <div> <div>step=10</div> <Slider step={10} marks={{ 0: '0', 10: '10', 20: '20', 30: '30', 40: '40', 50: '50', 100: '100' }} defaultValue={[10, 100]} range={true}></Slider> <br/> <br/> <div>step=0.1</div> <Slider step={0.1} marks={{ 0.1: '0.1', 0.2: '0.2', 0.3: '0.3', 0.4: '0.4', 0.5: '0.5' }} min={0} max={1} defaultValue={[0.1, 0.5]} range={true}></Slider> <br/> <br/> <div>Marks</div> <Slider marks={{ 20: '20°C', 40: '40°C' }} defaultValue={[0, 100]} tipFormatter={v => (`${v}°C`)} range={true} getAriaValueText={(value) => `${value}°C`}></Slider> <br/> <br/> <div>Included</div> <Slider marks={{ 20: '20°C', 40: '40°C' }} included={false} defaultValue={[0, 100]} range={true} tipFormatter={v => (`${v}°C`)} getAriaValueText={(value) => `${value}°C`}></Slider> </div> ); ``` ### 分段背景 通过使用 `linear-gradient` 及 `railStyle` ,配合 onChange 可以实现动态的分段背景效果。 ```jsx live=true import React, { useState, useMemo } from 'react'; import { Slider } from '@douyinfe/semi-ui'; () => { const [value, setValue] = useState([20, 60]); const changeValue = (newValue) => { setValue(newValue); }; const getRailStyle = (range) => { // color of second segment inherits from .semi-slider-track const color = ['var(--semi-color-danger)', 'transparent', 'var(--semi-color-success)']; const gradientPos = value.map(val => ((val - range[0]) / (range[1] - range[0])).toFixed(2) * 100 ); const style = { background: `linear-gradient(to right, ${color[0]} ${gradientPos[0]}%, ${color[1]} ${gradientPos[0]}%, ${color[1]} ${gradientPos[1]}%, ${color[2]} ${gradientPos[1]}%)` }; return style; }; const range = [10, 100]; const railStyle = useMemo(() => getRailStyle(range), [value, range]); return ( <Slider range min={range[0]} max={range[1]} onChange={changeValue} railStyle={railStyle} value={value} /> ); }; ``` ### 受控组件 滑块位置即 `Slider` 的值由 value 控制,配合 onChange 使用。 ```jsx live=true hideInDSM import React, { useState } from 'react'; import { Slider, Button } from '@douyinfe/semi-ui'; () => { const [value, setValue] = useState(10); const changeValue = () => { setValue(value + 10); }; return ( <div> <Button onClick={changeValue} style={{ marginRight: 20 }}>点击改变value值</Button> <br/> <br/> <Slider value={value}></Slider> </div> ); }; ``` ### 垂直 ```jsx live=true import React from 'react'; import { Slider } from '@douyinfe/semi-ui'; () => ( <div> <div style={{ height: 300, marginLeft: 30, marginTop: 10, paddingRight: 30, display: 'inline-block' }}> <Slider vertical></Slider> </div> <div style={{ height: 300, marginLeft: 30, marginTop: 10, paddingRight: 30, display: 'inline-block' }}> <Slider vertical verticalReverse></Slider> </div> <div style={{ height: 300, marginLeft: 30, marginTop: 10, paddingRight: 30, display: 'inline-block' }}> <Slider vertical range defaultValue={[20, 60]}></Slider> </div> <div style={{ height: 300, marginLeft: 30, marginTop: 10, paddingRight: 30, display: 'inline-block' }}> <Slider vertical verticalReverse range defaultValue={[20, 60]}></Slider> </div> <div style={{ height: 300, marginLeft: 30, marginTop: 10, paddingRight: 30, display: 'inline-block' }}> <Slider vertical range marks={{ 20: '20°C', 40: '40°C' }} step={10} defaultValue={[20, 60]}></Slider> </div> <div style={{ height: 300, marginLeft: 30, marginTop: 10, paddingRight: 30, display: 'inline-block' }}> <Slider vertical verticalReverse range marks={{ 20: '20°C', 40: '40°C' }} step={10} defaultValue={[20, 60]}></Slider> </div> </div> ); ``` ### 滑块带圆点 ```jsx live=true <div> <div> <div>Default</div> <Slider showBoundary={true} handleDot={{size:'4px',color:'blue'}}></Slider> </div> <br/> <br/> <div> <div>Range</div> <Slider defaultValue={[20, 60]} range handleDot={[{size:'4px',color:'blue'},{size:'4px',color:'pink'}]}></Slider> </div> </div> ``` ## API参考 | 属性 | 说明 | 类型 | 默认值 | 版本 | |------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|--------|--------| | aria-label | [aria-label](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label)属性,用来给当前元素加上的标签描述, 提升可访问性 | string | - | - | | aria-labelledby | [aria-labelledby](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-labelledby)属性,表明某些元素的 id 是某一对象的标签。它被用来确定控件或控件组与它们标签之间的联系, 提升可访问性 | string | - | - | | aria-valuetext | [aria-valuetext](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-valuetext)属性,为滑块的当前值提供用户友好的名称。 | string | - | - | | defaultValue | 设置初始取值 | number \| number[] | 0 | - | | disabled | 滑块是否禁用 | boolean | false | - | | handleDot | 滑块是否带有圆点 | { color: string, size: string} \| <br/> { color: string, size: string}[] | - | 2.52.0 | | included | `marks` 不为空对象时有效,值为 true 时表示值为包含关系,false 表示并列 | boolean | true | - | | marks | 刻度,key 的类型必须为 `number` 且取值在闭区间 \[min, max] 内 | Record<number, string \> | 无 | - | | max | 最大值 | number | 100 | - | | min | 最小值 | number | 0 | - | | railStyle | 滑块轨道的样式 | CSSProperties | - | - | | range | 是否支持两边同时可滑动 | boolean | false | - | | showArrow | tooltip 是否带箭头 | boolean | true | 2.48.0 | | showBoundary | 是否在 hover 时展示最大值最小值 | boolean | false | - | | showMarkLabel | 是否隐藏标签 | boolean | true | 2.48.0 | | step | 步长 | number | 1 | - | | tipFormatter | 设置Tooltip的展示格式,默认显示当前选值 | (value: string \| number \| boolean \| (string \| number \| boolean)[]) => any | v => v | - | | tooltipOnMark | 滑轨上的 mark 是否带有 tooltip | false | 2.48.0 | | tooltipVisible | 是否始终显示Tooltip | boolean | 无 | - | | value | 设置当前取值 | number \| number[] | | - | | vertical | 是否设置方向为垂直 | boolean | false | - | | verticalReverse | 反转垂直方向,即上大下小 | boolean | false | - | | onAfterChange | 值变化后触发,把当前值作为参数传入 | (value: number \| number[]) => void | 无 | - | | onChange | 当 Slider 的值发生改变时的回调 | (value: number \| number[]) => void | 无 | - | | onMouseUp | 鼠标松开滑块时触发 | (e: React.MouseEvent<HTMLDivElement\>) => void | 无 | 2.41.0 | | getAriaValueText | 用于给滑块的当前值提供一个用户友好的名称,对屏幕阅读器用户很重要,参数value为当前滑块的值,index为当前滑块的顺序 | (value: number, index?: number) => string | - | - | ## RTL/LTR - Slider 的水平方向支持 RTL,可通过 [ConfigProvider](/zh-CN/other/configprovider) 配置 `direction="rtl"` 启用。 - RTL 模式下,Slider 水平方向的最小值在右侧,最大值在左侧,滑块拖动方向与 LTR 相反。 - 垂直方向(`vertical`)不受 RTL 影响。 - RTL 模式下,键盘方向键的行为会镜像:`左箭头`/`下箭头` 增加值,`右箭头`/`上箭头` 减少值。 ## Accessibility ### ARIA - Slider 可聚焦的控制元素 role 为 `slider`。 - 元素的 `aria-valuenow` 属性为当前值的十进制数值。 - 元素的 `aria-valuemin` 属性为最小允许值的十进制数值。 - 元素的 `aria-valuemax` 属性为最大允许值的十进制数值。 - 当 Slider 为纵向时,元素的 `aria-orientation` 属性为 'vertical'。 - 当 `aria-valuenow` 的值不容易被理解时,支持通过 API `aria-valuetext` 传递一个字符串使其更友好。也可以通过 API `getAriaValueText(value, index)` 方法得到 `aria-valuetext` 的值。 - 支持通过 API `aria-label` 或者 `aria-labelledby` 确定 slider 的标签。 ### 键盘和焦点 - Slider 的滑块可被获取到焦点,并展示当前滑块的提示信息,且这些信息需要被辅助技术读取到。 - 当用户使用 `range` 属性时,可以使用 `Tab` 及 `Shift` + `Tab` 切换左右两个滑块的焦点。 - 键盘用户可以通过 `上箭头` 或 `右箭头` 来增加滑块值,`下箭头` 或 `左箭头` 来减少滑块值。 - 若想要滑块高于步长的变化量时, slider支持 10*step 的变化量: - 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 键聚焦) | 使用 `:focus-visible` 伪类,仅在键盘聚焦时显示 outline | | 滑动条圆形描边颜色 - 激活态 | `--semi-color-focus-border` | 鼠标点击/拖动 | 使用 `.semi-slider-handle-clicked` 类,在鼠标交互时显示 border | 如果你希望在主题配置中修改圆形按钮的聚焦样式,请注意上述两个变量对应不同的交互场景。 <DesignToken/>