ant-design Slider 分段刻度滑块(marks)实战:included 区间语义与 step=null 精确取值
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design
本文基于 ant-design 仓库中 Graduated slider(分段滑块)示例 及其配套源码展开,系统讲解通过marks属性构建带刻度标注的滑块、included的区间包含语义、range双滑块场景,以及当step=null时滑块只允许停留在marks、min、max等离散有效点上的底层机制。读完你既能直接照抄示例代码,也能理解这些行为在 Slider 入口源码 与 单测用例 中是如何被定义与验证的。
一、这个示例解决什么问题
普通 Slider 是一个连续区间选择器,滑块可以在min与max之间按step步进滑动。而"分段刻度滑块"(Graduated slider)在轨道上额外绘制一组带文案的刻度点,用刻度来"标注"数值含义。典型场景包括:
- 温度档位选择:
0°C / 26°C / 37°C / 100°C,不同刻度点还允许单独设置颜色与加粗样式; - 评分、难度、音量等枚举型取值,让用户既看到当前值,又明白每个档位的含义;
- 医学、测量类表单中需要精确吸附到"合法检测点"的取值(如心率档位只能落在仪器支持的值上)。
该演示页面被收录于 Slider 组件文档 的 Examples 列表(标题为 Graduated slider),对应的说明文件即components/slider/demo/mark.md,配套可运行代码为 mark.tsx。它一共展示了四组配置:included=true(含 range 双滑块)、included=false、marks + step、step=null,覆盖了 marks 属性最核心的几种用法。
二、marks 的数据结构与合法取值约束
根据 Slider API 文档,marks的完整类型定义为:
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| marks | 滑块的刻度标记。键的类型必须是number,且必须位于闭区间[min, max]内。每个刻度可以声明自己的样式 | { number: ReactNode } \| { number: { style: CSSProperties, label: ReactNode } } | - |
| included | 仅在marks不为空时生效。true表示区间为包含关系(默认),false表示不同标记之间是并列关系 | boolean | true |
| step | 滑块步进的粒度,必须大于 0,且能被(max - min)整除。当step为null且存在marks时,有效点将只有marks、min与max | number \| null | 1 |
两个关键约束值得展开:
- 键必须是数字且落在
[min, max]内:marks 的对象键即刻度对应的滑块数值,非数字键或越界值不会被当作有效刻度渲染; - 每个刻度可声明独立样式:标签既可以是简单的
ReactNode(如字符串'0°C'),也可以是{ style, label }对象——style作用于刻度文案 DOM,label可以是任意 React 节点(示例里甚至用了<strong>)。
在类型层面,示例通过SliderSingleProps['marks']显式标注了 marks 的类型,该类型在 Slider 入口 中被定义为SliderMarks = RcSliderProps['marks'],即最终透传给底层@rc-component/slider的类型;同时在 SliderBaseProps 中step?: null | number、marks?: SliderMarks、included?: boolean均为可选,说明"不带 marks 的连续滑块"与"带 marks 的分段滑块"共享同一组件与同一套渲染管线。
三、完整示例代码与四组配置拆解
下面代码取自 mark.tsx,为便于阅读理解我按官方示例原样整理并添加分组说明:
import React from 'react'; import { Slider } from 'antd'; import type { SliderSingleProps } from 'antd'; const style: React.CSSProperties = { marginBottom: 16, }; const sliderStyle: React.CSSProperties = { marginBottom: 48, }; // 1) marks:键为数值,值可为 ReactNode,也可为 { style, label } 对象 const marks: SliderSingleProps['marks'] = { 0: '0°C', 26: '26°C', 37: '37°C', 100: { style: { color: '#f50' }, label: <strong>100°C</strong>, }, }; const App: React.FC = () => ( <> {/* 2) included=true:选中区间被连续填充(默认行为) */} <h4 style={style}>included=true</h4> <Slider style={sliderStyle} marks={marks} defaultValue={37} /> <Slider style={sliderStyle} range marks={marks} defaultValue={[26, 37]} /> {/* 3) included=false:不同标记之间为并列关系,轨道不再填充区间 */} <h4 style={style}>included=false</h4> <Slider style={sliderStyle} marks={marks} included={false} defaultValue={37} /> {/* 4) marks + step:数值型 step 与 marks 同时生效 */} <h4 style={style}>marks & step</h4> <Slider style={sliderStyle} marks={marks} step={10} defaultValue={37} /> {/* 5) step=null:可选值仅为 marks、min、max */} <h4 style={style}>step=null</h4> <Slider style={sliderStyle} marks={marks} step={null} defaultValue={37} /> </> ); export default App;代码中值得注意的点:
defaultValue/value指定滑块位置:文档(mark.md)明确指出"使用value/defaultValue指定滑块位置"。这里defaultValue={37}让滑块初始落在37°C刻度上,range模式下则传入数组[26, 37]让左右两个手柄分别落在两个刻度;- 刻度个性样式:
100°C使用对象形式,将其标签渲染为红色(#f50)加粗的<strong>,说明 marks 天然支持"重要档位高亮"这类信息层级设计; - 同一套 marks 被四组滑块复用:marks 只是静态标注数据,真正的交互行为由
included、step、range等属性决定。
四、included=true / false:区间包含语义
included是理解分段滑块视觉反馈的关键。Slider 的值本身既可以是"某一点",也可以是一个"区间"(range 双手柄之间的范围)。
included=true(默认):表示"包含关系"。当滑块值为某一点时,从min(或左/下手柄)到当前值之间的轨道会被填充为主题色,直观表达"已选择的范围"。示例第一行滑块defaultValue={37}即从轨道起点填充到 37 刻度;included=false:表示"并列/协调关系"(文档原文coordinative),不同刻度之间相互独立、不存在"被包含的连续区间",因此轨道不会被连续填充,选中态以刻度点本身呈现。这类语义适合"从多个并列档位里选一个、而不是选一段连续区间"的场景。
从渲染结果看,只要传入 marks 且marks非空,根节点即会携带ant-slider-with-marks样式类,这在 快照测试 中可以观察到(快照中 marks 示例分组marks & step下的 DOM 根类名同时包含ant-slider-horizontal与ant-slider-with-marks)。
五、range 模式:双滑块同时吸附刻度
当滑块处于range(range 属性为true)时,value/defaultValue从number变为[number, number]数组,双手柄之间形成选中区间:
<Slider range marks={marks} defaultValue={[26, 37]} />在手柄可拖拽移动的过程中,每个手柄都遵循 marks 相关规则。类型层面可以在 SliderRangeProps 看到:range: true | SliderRange,value/defaultValue为数组类型,onChange/onChangeComplete回调携带的也是数组;而单值模式的 SliderSingleProps 则限定range?: false,取值与回调均为单一number。这意味着 marks 与 range 的组合(即"在分段刻度滑块上做区间选择",示例第二行)在 API 设计上是开箱即用的,不需要额外开关。
六、step=null 时的离散取值语义(重点)
marks与step的协同关系是本文最值得深入的部分,官方文档mark.md给出的结论是:
当
step=null时,Slider 的可选值仅有marks、min和max。
这句话的实操含义是:
- 用户拖拽手柄时,手柄会像"打点"一样只能停留在
marks中的每个键、以及轨道的两端min与max上,任何"两刻度之间"的中间值都不可达; - 若某个
marks键恰好等于min或max(示例中0°C就是min,100°C就是max),有效点集合即与刻度集合重合; - 语义上它等价于一个"固定档位"选择器,但外观仍是滑块轨道 + 刻度文案。
这一行为有明确的自动化测试背书。Slider 单测 中专门有一条用例验证:
it('when step is null, thumb can only be slid to the specific mark', () => { const intentionallyWrongValue = 40; // 故意给一个不落在 marks 上的默认值 const marks = { 0: '0', 48: '48', 100: '100', }; const { container } = render( <Slider marks={marks} defaultValue={intentionallyWrongValue} step={null} tooltip={{ open: true }} />, ); expect(container.querySelector('.ant-slider-handle')!.getAttribute('aria-valuenow')).toBe('48'); });该用例断言:即使开发者传入的defaultValue={40}并不在任何刻度上,在step=null下渲染后的手柄aria-valuenow也只会是48(离 40 最近的合法刻度),从无障碍属性层面印证了"有效点仅限 marks/min/max、非法中间值会被吸附到最近合法刻度"的实现事实。
与之对照的是同文件中另外两条用例(index.test.tsx):
step={1}时defaultValue={49}保持49(可按整数步进自由取值);step为undefined(不传,即默认值 1)时同样保持49。
三组用例对比可以清晰地得出规则:只要step是数值(含默认值 1),滑块就以步进为单位自由取值;只有当显式传入step={null}且存在 marks 时,才退化为"仅刻度可停靠"的离散模式。
顺带一提 API 文档(index.en-US.md)对step还有一个数值约束:"必须大于 0,并且能被(max - min)整除"。示例中max - min = 100、step={10},二者整除,因此数值刻度与 marks 可以同时正常工作(即第四组marks & step:滑块按 10 步进,但刻度文案仍按 marks 绘制)。
七、使用建议与易错点小结
结合文档、示例与源码,实际使用分段滑块时建议注意以下几点:
- 区分
step与marks的作用边界:marks只负责"标注 + 定义离散可停靠点(配合step=null)",step负责"连续区间内的步进粒度"。二者可以同时使用,但只有当step为null时 marks 才是唯一的取值约束; included=false别忘了:如果设计上要求"并列档位单选"而非"连续区间包含",务必显式设置included={false},否则默认填充的连续轨道会带来错误的"区间选择"暗示;- 刻度键务必在
[min, max]闭区间内且为数字,否则不会成为有效刻度;期望最大刻度与max一致时,直接让键等于max(示例中的100)即可; - 受控时用
value,非受控时用defaultValue,数值类型与range保持一致(单值为number、range 为二元数组),回调优先使用onChange+onChangeComplete(旧的onAfterChange在类型中已标记为 deprecated,见 index.tsx); - 验证离散取值时以
aria-valuenow为准,这也是仓库单测采用的断言手段,对无障碍与自动化测试都有实际意义。
如需继续深入,可继续阅读 Slider 完整 API 文档 中的tooltip、reverse、vertical等属性,或参考本示例同目录下的 垂直滑块示例 与 反向示例,理解 marks 在垂直 / RTL 布局下的刻度渲染行为。
【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/GitHub_Trending/an/ant-design
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考