跳到主要内容
menumenu tanreiexpand_morecontrasttranslate
tanreiexpand_more
开始使用WebReactSwiftUIJetpack ComposeFlutterReact Native预设你自己的设计系统与 AI 代理一起使用 Tanrei品牌设计令牌设计系统与定制Theme Studio颜色字体排印图标形状层级动效交互状态行为布局无障碍文字颜色令牌字阶令牌形状令牌层级令牌动效令牌状态令牌间距与度量令牌组件按钮主操作按钮分段按钮按钮组带菜单的按钮工具栏复制按钮复选框单选按钮开关纸片(Chip)菜单滑块滑块字段日历DateTimePicker字段组单选组复选框组下拉选择框下拉选择字段菜单栏文件上传文件选择器 进阶放置区文本输入框 进阶文本字段纸片字段字段组合框 进阶组合框字段数字输入框 进阶数字字段颜色输入框 进阶颜色字段一次性验证码输入框 进阶一次性验证码字段嵌入式文本输入框 进阶聊天输入栏卡片分隔线模态层 进阶对话框列表列表行底部面板侧边面板轮播代码块表格弹出框步骤折叠面板描述列表图示应用外壳页头树视图时间线图片预览调整手柄可调整大小的框滚动区域徽标进度指示器计量条数字滚动器加载指示器消息条(Snackbar)工具提示键盘快捷键标签(Tag)行内代码提示块(Callout)头像空状态标签组(Tag group)骨架屏文件项色块跑马灯文本标签页标签页视图应用栏导航栏导航抽屉搜索链接分页上一页/下一页链接步骤条面包屑目录链接卡片FAB 菜单状态Tanrei 是如何构建的参与贡献许可与致谢
首页组件滑块

滑块

让用户沿轨道拖动手柄,以连续或步进的方式选择一个值或一个取值范围。

content_copy以 Markdown 格式查看
类别
选择
令牌
comp.slider
层级
基础

概览

Web
tanrei-slider
schedule
SwiftUI
TanreiSlider
schedule
Jetpack Compose
TanreiSlider
schedule
Flutter
TanreiSlider
schedule
React Native
Slider
schedule

生成自 docs/status/status.json, specs/slider.spec.json

连续、带刻度点的步进,以及范围

交互演示

每个控件设置实时组件的一个属性,代码也随之更新。设置会保留在页面地址中,打开链接即可看到相同的状态。 代码适用于 Web。其他平台请参阅“各平台名称对照”。

用法

滑块在 min 和 max(默认为 0 和 100)之间选择一个值。设置 range 后,滑块有两个手柄,选择的是从 startValue 到 value 的区间。step 为 0 时是连续的;大于 0 时,取值会吸附到 min 加上 step 的整数倍。

滑块不绘制标签。label 为辅助技术给它命名;可见的标签是你自己放在滑块旁边的文本,用词与之相同。范围滑块还需要为起始手柄提供 startLabel。

有两个事件报告变化。用户拖动期间,每次变化都会触发 input;变化结束时触发一次 change:按下结束了,或者进行了一次按键或辅助技术的调整。你自己设置的值两者都不触发。各平台按自己的方式为它们命名:Compose 上是 onValueChange 和 onValueChangeFinished,Flutter 上是 onChanged 和 onChangeEnd,React Native 上是 onValueChange 和 onSlidingComplete,SwiftUI 上则是绑定和 onCommit。

让滑块的手柄变成圆形的那次修订,以及把它的圆环加粗的那次评审,改变的都只是外观,所以下面的示例全部适用于当前版本。Web 绘制的是当前的外观。SwiftUI、Compose、Flutter 和 React Native 待跟进:在跟上之前,它们仍然绘制以前的外观——一个实心的圆形手柄,轨道在手柄边缘处截断,所有尺寸都用同一种大小的刻度圆点。

<!-- 滑块不绘制标签:把可见的标签放在它旁边,用词相同。 -->
<tanrei-slider name="volume" label="音量" value="40" value-indicator="active"></tanrei-slider>

<!-- 步进,每一步上有一个圆点。 -->
<tanrei-slider label="亮度" value="30" step="10" stops></tanrei-slider>

<!-- 范围滑块以同一个 name 先提交 start-value,再提交 value。 -->
<tanrei-slider name="price" range min="0" max="500" step="10" start-value="100" value="300"
  label="最高价格" start-label="最低价格"></tanrei-slider>

<script type="module">
  import '@tanrei/web/components/slider';
  const volume = document.querySelector('[name="volume"]');
  // 元素先设置自己的 value,再触发 input 和 change。
  volume.addEventListener('input', () => preview(volume.value));
  volume.addEventListener('change', () => save(volume.value));
</script>
import { Slider } from '@tanrei/react';

<Slider
  label="音量"
  value={volume}
  valueIndicator="active"
  onInput={(e) => setVolume(e.currentTarget.value)}
  onChange={(e) => save(e.detail.value)}
/>

<Slider
  range
  min={0}
  max={500}
  step={10}
  startValue={minPrice}
  value={maxPrice}
  label="最高价格"
  startLabel="最低价格"
  onInput={(e) => setPrices(e.currentTarget.startValue, e.currentTarget.value)}
/>
import Tanrei

// 每次变化都会写入绑定;变化结束时运行 onCommit。
TanreiSlider("音量", value: $volume, in: 0...100, onCommit: save)

TanreiSlider("亮度", value: $brightness, in: 0...100, step: 10)

// 范围滑块使用绑定为 ClosedRange 的构造器:lowerBound 是起始手柄。
TanreiSlider("最高价格", startLabel: "最低价格", value: $prices, in: 0...500, step: 10)
import io.github.chnotchy.tanrei.components.TanreiSlider

// 状态提升:在 onValueChange 里更新 value。
TanreiSlider(
    value = volume,
    onValueChange = { volume = it },
    contentDescription = "音量",
    onValueChangeFinished = ::save, // 与 foundation 的不同,它带有值
)

// step 是步长,不是 Material 的步数。
TanreiSlider(value = brightness, onValueChange = { brightness = it }, step = 10f, contentDescription = "亮度")

// 范围滑块使用 value 为 ClosedFloatingPointRange 的重载。
TanreiSlider(
    value = prices,
    onValueChange = { prices = it },
    valueRange = 0f..500f,
    step = 10f,
    contentDescription = "最高价格",
    startContentDescription = "最低价格",
)
import 'package:tanrei/tanrei.dart';

TanreiSlider(
  value: volume,
  onChanged: (double value) => setState(() => volume = value), // 传 null 则禁用
  onChangeEnd: save,
  semanticLabel: '音量',
);

// step 是步长,不是 Material 的 divisions。
TanreiSlider(value: brightness, step: 10, onChanged: (double value) => setState(() => brightness = value), semanticLabel: '亮度');

TanreiSlider.range(
  start: minPrice,
  end: maxPrice,
  max: 500,
  step: 10,
  onChanged: (double start, double end) => setState(() {
    minPrice = start;
    maxPrice = end;
  }),
  semanticLabel: '最高价格',
  startSemanticLabel: '最低价格',
);
import { Slider } from '@tanrei/react-native';

<Slider value={volume} onValueChange={setVolume} onSlidingComplete={save} accessibilityLabel="音量" />

<Slider value={brightness} step={10} stops onValueChange={setBrightness} accessibilityLabel="亮度" />

// 范围滑块:两个回调都接收 (value, startValue)。
<Slider
  range
  minimumValue={0}
  maximumValue={500}
  step={10}
  startValue={minPrice}
  value={maxPrice}
  onValueChange={(value, startValue) => setPrices(startValue, value)}
  accessibilityLabel="最高价格"
  startAccessibilityLabel="最低价格"
/>

规格

尺寸、居中、数值指示器、纵向和禁用

在默认系统中,滑块的手柄是一个圆环:一个直径 20、以表面颜色填充的圆,外面是一圈活动轨道颜色的轮廓。轮廓宽 3,画在圆的边缘以内,所以不改变手柄的大小:它留下一个直径 14 的表面颜色圆面,轮廓更宽,留下的圆面就更小。轨道和活动轨道从手柄下方穿过,在它的中心相接,所以活动轨道看起来是一条终止于圆环的线。在 4、6 和 8 的轨道上,也就是在每种尺寸下,手柄都是 20。

刻度圆点,以及标记行程终点的那个圆点,粗细是轨道的一半:小号、中号和大号分别为 2、3 和 4。数值指示器位于圆环顶部略靠上的位置,与圆环居中对齐。禁用时,圆环保留它的填充,轮廓则随活动轨道一起变淡。

设计系统可以用同一组令牌按自己的方式绘制手柄。轮廓为 0 时,手柄是一个实心的形状;轨道与手柄之间留有间隙时,轨道在到达手柄之前就终止,并在那里取圆角。m3-like 就是这样画出 Material 的竖条的。

构成

构成

绘制在 Web 包所绘的测试样例 continuous 之上。
部件 说明
1 track The line the handle travels along: the whole range from min to max. Drawn in the track color where it is not active.
2 active-track The part of the track from the origin to the value, or between the two handles of a range, drawn over the track.
3 handle The part the person drags, one per value: in Tanrei a circle (its width along the track equals its height across it) with a fill and an outline of its own, a narrow upright bar where a system says so.
4 handle-outline · 可选 The ring drawn over the handle's fill, inside its edge, so the handle keeps its size however wide the ring is: a wider ring leaves a smaller disc of fill. None where its width is 0.
5 container The round state layer centered on a handle, moving with it. It carries hover, focus and press and is invisible at rest.
6 stop-indicator · 可选 A dot on the track's midline, half the track's thickness across in Tanrei: at an end of the handle's travel that lies on the track, and at every step while stops is true.
7 value-indicator · 可选 A small label beside a handle that shows its value while the handle is pressed or focused, or always.
8 focus-ring Gap-then-ring indicator around the handle that has keyboard focus.

尺寸

尺寸

长度 令牌 值(px)
handle height tanrei.comp.slider.handle.height.small 20
container size tanrei.comp.slider.container.size 40
绘制在 Web 包所绘的测试样例 continuous 之上。

属性

属性 类型 默认值 说明
size small | medium | large "small" The track's weight and the stop dots' size (and, where a system says so, the handle's height). Small is the everyday slider; medium and large give a prominent control (a volume or brightness slider) more weight. The hit area always meets the minimum target. A design system may add values.
orientation horizontal | vertical "horizontal" Horizontal runs from the inline start (min) to the inline end (max); vertical runs from the bottom (min) to the top (max).
value number 0 The value of the only handle, or of a range's end handle.
startValue number 0 The value of a range's start handle. Ignored while range is false.
range boolean false Draws two handles, choosing the range from startValue to value (a price filter).
min number 0 The lowest value, at the track's start.
max number 100 The highest value, at the track's end.
step number 0 0 is continuous. Above 0 the slider is discrete: its values are min plus whole multiples of step, up to max.
origin number null Where the active track starts. Null starts it at min. A value inside the range makes a centered slider (a balance control) whose active track grows toward either side. Ignored while range is true.
stops boolean false Draws a stop dot at every value a discrete slider can take. Meant for a few steps; ignored while step is 0.
valueIndicator none | active | always "none" Whether a handle shows its value beside it: never, while it is pressed or has keyboard focus, or all the time.
disabled boolean false Prevents interaction and removes the handles from the focus order.
label string "" Names the slider (a range's end handle) for assistive technology. Never drawn: a visible label is the product's text next to the slider, in the same words. Required.
startLabel string "" Names a range's start handle ("Minimum price"). Required while range is true.
valueText string "" The value as words ("40 %", "3 stars"), spoken and shown by the value indicator in place of the number. For a range, the end handle's.
startValueText string "" The start handle's valueText, for a range.

事件

事件 说明
input The person changed a value; carries value and, for a range, startValue. Emitted for every change during a drag, not when the product sets a value.
change The person finished changing a value (the press ended, or a key or assistive-technology adjustment was made); carries value and, for a range, startValue. Always after the input it follows.

状态

行为

以 EARS 形式写成的需求。每个平台的实现都按这些 ID 逐条测试。需求原文取自规格文件,原样以英文呈现。

ID 需求
SLD-1 THE slider SHALL keep value (and, while range is true, startValue) between min and max; WHILE step is greater than 0 it SHALL take only min plus whole multiples of step that do not exceed max, snapping to the nearest, and WHILE step is 0 any value between min and max.
SLD-2 WHEN the person presses the slider by pointer or touch — the track, a handle, or the space around them — THE slider SHALL move the handle nearer the press (for a range whose handles meet, the one that can move toward the press) to the value under it and, WHILE the press moves, SHALL make the handle follow it along the track, clamped to the handle's range and snapped to step; it SHALL emit input once for each value change and, WHEN the press ends with a value other than the one it started with, change once after the last input. On touch the handle SHALL move once the press has travelled along the track by the platform's touch slop, or when a press that did not move ends; a press that first travels across the track by the slop SHALL be left to an enclosing scroll view and SHALL change nothing.
SLD-3 WHILE a handle has keyboard focus THE slider SHALL increase its value by one keyboard step with Right Arrow or Up Arrow and decrease it with Left Arrow or Down Arrow (Right and Left swapped in a right-to-left horizontal slider), by ten keyboard steps with Page Up and Page Down, and set it to the lowest and highest value it can take with Home and End; the keyboard step is step, or one hundredth of max − min while step is 0. Each key press that changes the value SHALL emit input and then change once; a key press that changes nothing SHALL emit nothing.
SLD-4 WHEN assistive technology increments or decrements a handle (a vertical swipe on an adjustable element, the platform's increment and decrement actions) THE slider SHALL change its value by one keyboard step, and WHEN it sets a value directly where the platform allows that THE slider SHALL clamp and snap it; either SHALL emit input and then change once.
SLD-5 WHILE range is true THE slider SHALL draw a start handle at startValue and an end handle at value, SHALL stop each handle at the other's value so that the two may meet but never cross, and SHALL make each handle its own Tab stop, the start handle first, and its own element for assistive technology.
SLD-6 WHILE range is false THE slider SHALL draw the active track between origin (min while origin is null, and origin clamped to the range) and value, on whichever side of origin value lies; WHILE range is true it SHALL draw the active track between startValue and value.
SLD-7 THE slider SHALL be as long along its axis as its layout gives it and, across it, as thick as the larger of comp.slider.container.size and comp.slider.handle.height.small for size small (and so on for each size); it SHALL extend its hit area across its axis to sys.target.touch for touch input and sys.target.pointer for fine pointers without changing that size (a platform that cannot tell the input type SHALL use sys.target.touch), the whole slider being the hit area, and a handle drawn smaller than the target SHALL NOT make the hit area smaller. It SHALL draw the track comp.slider.track.thickness.small thick for size small (and so on), centered across the axis and inset from each end of the slider by half of comp.slider.container.size, so that a handle and its state layer stay inside the slider at min and max; a handle's center SHALL travel between the two points half the track's thickness in from the track's ends, concentric with its rounded ends.
SLD-8 THE slider SHALL draw the track in comp.slider.standard.track.color and the active track over it in comp.slider.standard.active-track.color. WHILE comp.slider.track.gap is 0 the track and the active track SHALL run under each handle and meet at its center, so that nothing lies between the active track and the handle; WHILE it is above 0 they SHALL stop that far from each side of each handle, measured from the handle's edge. The pieces SHALL be rounded by comp.slider.track.shape.small for size small (and so on), clamped to half the thickness, at the track's two ends and, WHILE comp.slider.track.gap is above 0, beside a handle; they SHALL be square where two pieces meet (under a handle while the gap is 0, and at an origin inside the travel). It SHALL draw each handle centered on the track's midline, comp.slider.standard.handle.width along the axis and comp.slider.handle.height.small across it for size small (and so on), rounded by comp.slider.handle.shape clamped to half its smaller side, painted as SLD-24 says, above the tracks — each part from the tokens of the state SLD-11 names.
SLD-9 THE slider SHALL draw a dot of comp.slider.stop-indicator.size.small for size small (and so on) centered on the track's midline at each end of the handles' travel that lies on the track rather than on the active track, and WHILE stops is true and step is greater than 0 also at every value the slider can take; a dot SHALL be drawn in comp.slider.standard.stop-indicator.color where it lies on the track and in comp.slider.standard.active-stop-indicator.color where it lies on the active track, SHALL NOT be drawn where a handle or its track gap covers it, and a size of 0 SHALL draw none.
SLD-10 THE slider SHALL draw each handle's container, a shape of comp.slider.container.size on each side with corners comp.slider.container.shape, centered on the handle and moving with it, filled with comp.slider.standard.container.color from the handle's state, behind the handle and above the tracks; it may extend beyond the track without affecting layout, and a size of 0 SHALL draw none.
SLD-11 WHILE a handle is hovered, keyboard-focused or pressed THE slider SHALL render that handle and its container from the component tokens of that handle's highest-precedence active state, with precedence pressed over focused over hovered, and the tracks, stop dots and opacity from the highest-precedence state of any of its handles; a handle is hovered while a fine pointer is over the slider and that handle is the one a press there would move; focus gained by pointer or touch SHALL NOT select the focused state. WHEN the colors change between states THE slider SHALL animate them over comp.slider.motion.state-duration with comp.slider.motion.state-easing, and at once where motion is reduced.
SLD-12 WHEN a handle receives keyboard focus THE slider SHALL show the focus ring around that handle without animation; focus gained by pointer or touch SHALL NOT show it.
SLD-13 WHEN the slider is pressed by pointer or touch THE slider SHALL show the pressed state of the handle the press moves without delay, SHALL keep it for as long as the press lasts, and SHALL keep it visible for at least sys.motion.duration.press-hold, even when the press ends sooner.
SLD-14 WHILE pressed THE slider SHALL answer as sys.behavior.pressed-feedback names: overlay applies the pressed tokens to the whole container at once; ripple grows the pressed state layer from the point of contact (from the center for keyboard activation); dim applies the pressed opacity to the whole control and draws no state layer; none draws no answer to the press. Under ripple and dim only the container color stays with the state under the press; handle, tracks, stop dots, opacity and shape still come from the pressed tokens. Under none every part stays with the state under the press. The ripple grows over comp.slider.motion.feedback-duration with comp.slider.motion.state-easing, fades over comp.slider.motion.state-duration with comp.slider.motion.state-easing once the press has ended and press-hold has passed, and appears whole where motion is reduced. The slider's point of contact is the pressed handle's center, because the handle moves to the press.
SLD-15 THE slider SHALL ease its container, handle and track corners by sys.shape.corner-smoothing (0 is a quarter circle, 0.6 the iOS continuous corner; a platform with only the OS curve draws it for any amount above 0) and show its keyboard focus as sys.behavior.focus-style names (ring or glow); a platform that cannot draw a choice SHALL fall back to the first value and state that in its README.
SLD-16 WHEN a value changes by a key, by assistive technology, by a press on the track that did not drag, or because the product set it, THE slider SHALL move the handle and the active track to it over comp.slider.motion.value-duration with comp.slider.motion.value-easing; WHILE a press drags a handle, the handle SHALL follow the press without delay; WHERE motion is reduced every change SHALL be shown at once. WHEN the product sets value or startValue THE slider SHALL NOT emit input or change.
SLD-17 WHILE valueIndicator is always, or valueIndicator is active and a handle is pressed or has keyboard focus, THE slider SHALL draw that handle's value indicator comp.slider.value-indicator.offset beyond the handle's outer edge across the axis — above the handle in a horizontal slider, at its inline start in a vertical one; for a round handle beyond the top of its circle, outline included, which lies half of comp.slider.handle.height.small from the track's midline for size small (and so on) — with its center on the handle's center along the axis, shifted along the axis only as far as it must to stay inside the window: the handle's value text on one line in comp.slider.value-indicator.label.typescale and comp.slider.value-indicator.label.color, centered in a container of comp.slider.value-indicator.container.color at least comp.slider.value-indicator.container.height high and as wide as the text plus comp.slider.value-indicator.container.padding-inline on each side but never narrower than it is high, rounded by comp.slider.value-indicator.container.shape. The value indicator SHALL be drawn above neighbouring content and above the handle and its container, without affecting layout or being clipped by the slider, SHALL take no focus and no input (a read overlay), SHALL be hidden from assistive technology, and SHALL fade in and out over comp.slider.motion.state-duration with comp.slider.motion.state-easing, at once where motion is reduced.
SLD-18 THE slider SHALL give each handle a value text: valueText for the only handle and a range's end handle, startValueText for a range's start handle; or, while that is empty, what the product's own function writes for that handle's value, WHERE the product gave the slider such a function in the platform's idiom; or, with neither, the value formatted as a number in the platform's locale with as many fraction digits as the keyboard step has, at most two; the value indicator SHALL show it and assistive technology SHALL read it as the handle's value. THE slider SHALL ask the function again at every change of a value and WHEN the function itself changes, SHALL take an empty answer as no answer, and SHALL announce nothing for a change of the words alone.
SLD-19 THE slider SHALL expose each handle as a slider (the adjustable trait on Apple platforms) named by label, or by startLabel for a range's start handle, with its value, its value text, the lowest and highest value it can take (for a range, bounded by the other handle), its orientation and its disabled state; the tracks, stop dots, container and value indicator SHALL be hidden from assistive technology. WHILE a handle's label (label, or startLabel for a range's start handle) is not empty THE slider SHALL expose it as that handle's accessible name, over a name the product gives the slider on the element (aria-label or aria-labelledby). WHILE label is empty and the product gives the slider a name of its own on the element — aria-label, or aria-labelledby naming an element — THE slider SHALL expose that name on each handle whose own label is empty. WHILE label is empty and the product gives it none, or range is true and startLabel is empty, THE slider SHALL still render, and in debug builds report that a handle has no accessible name: one name of the product's does not tell a range's two handles apart.
SLD-20 WHILE disabled is true THE slider SHALL NOT change a value or emit input or change, SHALL NOT be focusable, SHALL show no value indicator, SHALL expose the disabled state to assistive technology, and SHALL render its container, handles, tracks, stop dots and opacity from the disabled component tokens.
SLD-21 WHILE orientation is vertical THE slider SHALL run its track from the bottom (min) to the top (max), be as tall as its layout gives it, and lay out every part SLD-7 to SLD-17 name turned a quarter turn; WHEN the layout direction is right-to-left THE slider SHALL run a horizontal track from the right (min) to the left (max), mirroring the active track, the stop dots and the arrow keys (SLD-3), and SHALL NOT mirror a vertical slider. WHILE a value indicator is drawn in a right-to-left layout THE slider SHALL keep it centred on its handle and shift it along the axis only as far as it must to stay inside the window, away from whichever edge of the window it would cross, as in left-to-right (SLD-17): the shift is physical and is not mirrored.
SLD-22 IF value, startValue or origin lies outside min and max, or off a step, THE slider SHALL draw and expose it clamped and snapped without emitting input or change; IF startValue is greater than value while range is true THE slider SHALL draw both handles at value; IF max is not greater than min THE slider SHALL draw its handles at the track's start and take no input; IF step is below 0 THE slider SHALL treat it as 0. In debug builds it SHALL report each of these.
SLD-23 WHEN size names a value that the active design system does not have, or orientation or valueIndicator a value the component does not define, THE slider SHALL render with the default value (small, horizontal, none), and in debug builds report the unknown value.
SLD-24 THE slider SHALL fill the whole shape of each handle with comp.slider.standard.handle.color and draw the handle's outline over the fill, comp.slider.handle-outline.width wide inside the handle's edge, in comp.slider.standard.handle-outline.color, so that the outline never changes the handle's width or height and a width of 0 draws none; it SHALL raise the handle by comp.slider.standard.handle.elevation. The parts under a handle (the tracks where they meet, a stop dot, the container) SHALL show through it only as far as its fill is translucent.
SLD-25 THE slider SHALL take its layout direction from where it is used — the direction of the nearest ancestor that sets one, and the platform's own where none does — and SHALL have no direction setting of its own; WHEN that direction changes while the slider is shown THE slider SHALL draw itself for the new direction at once, without a motion of its own, keeping its values and its focus, each value indicator placed again by the new direction.
SLD-26 THE slider SHALL lay out a handle's value text in its value indicator as text of its own direction, isolated from the text around it: each takes the base direction of its first strong character, or the layout direction where it has none, so that a left-to-right word, a number with its sign or unit, a telephone number and an address keep their own order in a right-to-left layout and reorder nothing beside them; THE slider SHALL still place the indicator and align its lines by the layout direction, not by the text's own.
SLD-27 WHERE the platform has ARIA attributes (the web), WHEN the product sets an aria- attribute on the host, by the attribute or by the ARIA property that reflects it, THE slider SHALL move it to the data-aria- attribute of the same name, so that the host is no second node with its name, description or state, and SHALL expose it on the element that carries its role (every handle carries them) — an id reference by the elements it names in the host's tree or a tree around it, an error message as part of the description while the slider is invalid — except those the slider owns (aria-disabled), which it SHALL move and not expose. WHEN the product removes the aria- attribute or its data-aria- twin, or sets the ARIA property that reflects it (ariaLabel, ariaDescribedByElements…) to null, THE slider SHALL take it off that element. WHEN the product sets an aria- attribute the slider owns THE slider SHALL report it in debug builds.

各平台名称对照

对等并不意味着 API 完全相同。同一份契约,由各平台以自己的惯用方式表达。

规格 Web SwiftUI Jetpack Compose Flutter React Native
组件 tanrei-slider TanreiSlider TanreiSlider TanreiSlider Slider
size · 属性 size size size size size
orientation · 属性 orientation orientation orientation orientation orientation
value · 属性 value value value value value
startValue · 属性 startValue value value start startValue
range · 属性 range value value range range
min · 属性 min bounds valueRange min minimumValue
max · 属性 max bounds valueRange max maximumValue
step · 属性 step step step step step
origin · 属性 origin origin origin origin origin
stops · 属性 stops showsStops showStops showStops stops
valueIndicator · 属性 valueIndicator valueIndicator valueIndicator valueIndicator valueIndicator
disabled · 属性 disabled disabled enabled onChanged disabled
label · 属性 label label contentDescription semanticLabel accessibilityLabel
startLabel · 属性 startLabel startLabel startContentDescription startSemanticLabel startAccessibilityLabel
valueText · 属性 valueText valueText stateDescription semanticValue valueText
startValueText · 属性 startValueText startValueText startStateDescription startSemanticValue startValueText
input · 事件 input value onValueChange onChanged onValueChange
change · 事件 change onCommit onValueChangeFinished onChangeEnd onSlidingComplete
备注 Attributes are kebab-case (start-value, value-indicator, start-label, value-text, start-value-text). Each handle is an element with role="slider" and its aria-value attributes, aria-orientation and aria-label in the shadow root (not ElementInternals, which axe cannot see). Not <input type="range">: it has one handle, no gap, no stop dots, and its parts cannot be styled alike in every engine. The handle's outline is its box's border with box-sizing: border-box, over its background (the fill), so it keeps its width and height (SLD-24); while track.gap is 0 the two pieces of the tracks meet at the handle's center under it (SLD-8). Form-associated: it takes name and submits value (a range submits startValue, then value, under the same name), and it sets its own value and startValue before firing input and change, as a native control does. Snapshot harness: the stages of SLD-7. The product's function for a value's words (SLD-18) is formatValue, a property and never an attribute: ((value: number) => string) | null, null by default; it is asked for each handle at every change of a value and when the property itself is set. Slider idiom: TanreiSlider(_ label:, value: Binding<Double>, in bounds: ClosedRange<Double> = 0...100, step:); a range is the initializer whose value is a Binding<ClosedRange<Double>> (lower bound startValue, upper bound value) and which also takes startLabel. Every change writes the binding, which is the input event. onCommit is Tanrei's own parameter, called when a change is finished (SLD-2, SLD-3, SLD-4), as the text field's is; SwiftUI's onEditingChanged reports a drag's start and end, not a change. SwiftUI's own Slider is not used: its track and handle cannot take the tokens. Each handle is an adjustable element (.accessibilityAdjustableAction, .accessibilityValue); disabled follows .disabled(_:). Lagging: ships an earlier form of the slider — a solid round handle, tracks cut at the handle's edge (square there while track.gap is 0), one stop-dot size for every size. It does not draw yet: the handle's fill and outline (SLD-24), the tracks meeting under the handle while track.gap is 0 (SLD-8), the stop dot per size (SLD-9) and the value indicator's placement over a round handle (SLD-17). Not built on this platform yet: the product's function for a value's words (SLD-18) is a format: ((Double) -> String)? = nil parameter. Not built on this platform yet: the stage color a fixture's surface names, which the continuous-on-container-highest fixture draws on. Compose idiom (as Slider and RangeSlider): TanreiSlider(value: Float, onValueChange, modifier, enabled, valueRange: ClosedFloatingPointRange<Float> = 0f..100f, step: Float = 0f, onValueChangeFinished, …); a range is the overload whose value is a ClosedFloatingPointRange<Float> (start and end) and which also takes startContentDescription and startStateDescription. step is a size, not Material's count of steps. State hoisting: the caller moves value from onValueChange. enabled is the inverse flag. Each handle has Compose's slider semantics (progressBarRangeInfo and setProgress), with stateDescription as its value text. Lagging: ships an earlier form of the slider — a solid round handle with its shape clipped out of the tracks, one stop-dot size for every size. It does not draw yet: the handle's fill and outline (SLD-24), the tracks meeting under the handle while track.gap is 0 (SLD-8), the stop dot per size (SLD-9) and the value indicator's placement over a round handle (SLD-17). Not built on this platform yet: the product's function for a value's words (SLD-18) is a format: ((Float) -> String)? = null parameter. Not built on this platform yet: the stage color a fixture's surface names, which the continuous-on-container-highest fixture draws on. Flutter idiom (as Slider): TanreiSlider(value:, onChanged:, onChangeEnd:, min: 0, max: 100, step: 0, …), with step a size rather than Material's divisions; a range is the named constructor TanreiSlider.range(start:, end:, onChanged: (start, end) {…}, …), since Flutter's RangeValues belongs to Material. A null onChanged disables it. orientation is Flutter's Axis. Each handle is a Semantics(slider: true, value:, increasedValue:, decreasedValue:, onIncrease:, onDecrease:) node; the widgets library draws everything else. Lagging: ships an earlier form of the slider — a solid round handle, tracks cut at the handle's edge and rounded there (a notch between the round end and the handle), one stop-dot size for every size. It does not draw yet: the handle's fill and outline (SLD-24), the tracks meeting under the handle while track.gap is 0 (SLD-8), the stop dot per size (SLD-9) and the value indicator's placement over a round handle (SLD-17). Not built on this platform yet: the product's function for a value's words (SLD-18) is format: String Function(double)?. Not built on this platform yet: the stage color a fixture's surface names, which the continuous-on-container-highest fixture draws on. Controlled, in the community slider's idiom (value, minimumValue, maximumValue, step, onValueChange, onSlidingComplete), without depending on it: React Native has no slider in its core. A range adds range and startValue, and both callbacks receive (value, startValue). Each handle is a view with accessibilityRole="adjustable", accessibilityValue ({ min, max, now, text }) and the increment and decrement accessibility actions; the drag is a the touch responder props (onStartShouldSetResponder and the rest) that yields to an enclosing ScrollView as SLD-2 says. The arrow keys work where a hardware keyboard is attached, as the tabs row's do. Lagging: ships an earlier form of the slider — a solid round handle, tracks cut at the handle's edge and rounded there (a notch between the round end and the handle), one stop-dot size for every size. It does not draw yet: the handle's fill and outline (SLD-24), the tracks meeting under the handle while track.gap is 0 (SLD-8), the stop dot per size (SLD-9) and the value indicator's placement over a round handle (SLD-17). Not built on this platform yet: the product's function for a value's words (SLD-18) is a formatValue prop, (value: number) => string. Not built on this platform yet: the stage color a fixture's surface names, which the continuous-on-container-highest fixture draws on.

令牌

令牌 引用 解析值(浅色)
--tanrei-comp-slider-container-shape sys.shape.corner.full 9999px
--tanrei-comp-slider-container-size — 40px
--tanrei-comp-slider-disabled-opacity — 1
--tanrei-comp-slider-focused-opacity — 1
--tanrei-comp-slider-handle-outline-width — 3px
--tanrei-comp-slider-handle-height-large — 20px
--tanrei-comp-slider-handle-height-medium — 20px
--tanrei-comp-slider-handle-height-small — 20px
--tanrei-comp-slider-handle-shape sys.shape.corner.full 9999px
--tanrei-comp-slider-hovered-opacity — 1
--tanrei-comp-slider-motion-feedback-duration sys.motion.role.feedback 450ms
--tanrei-comp-slider-motion-state-duration sys.motion.role.state.compact 200ms
--tanrei-comp-slider-motion-state-easing sys.motion.easing.state cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-slider-motion-value-duration sys.motion.role.selection.travel 250ms
--tanrei-comp-slider-motion-value-easing sys.motion.easing.spatial cubic-bezier(0.2, 0, 0, 1)
--tanrei-comp-slider-pressed-opacity — 1
--tanrei-comp-slider-standard-active-stop-indicator-color sys.color.on-primary #ffffff
--tanrei-comp-slider-standard-active-track-color sys.color.primary #1b1b1b
--tanrei-comp-slider-standard-container-color — #000000 · 0%
--tanrei-comp-slider-standard-disabled-active-stop-indicator-color — #ffffff
--tanrei-comp-slider-standard-disabled-active-track-color — #1b1b1b · 38%
--tanrei-comp-slider-standard-disabled-container-color — #000000 · 0%
--tanrei-comp-slider-standard-disabled-handle-outline-color — #1b1b1b · 38%
--tanrei-comp-slider-standard-disabled-handle-color — #ffffff
--tanrei-comp-slider-standard-disabled-handle-elevation —
--tanrei-comp-slider-standard-disabled-handle-width — 20px
--tanrei-comp-slider-standard-disabled-state-layer-color — #1b1b1b · 38%
--tanrei-comp-slider-standard-disabled-stop-indicator-color — #1b1b1b · 38%
--tanrei-comp-slider-standard-disabled-track-color — #1b1b1b · 12%
--tanrei-comp-slider-standard-focused-active-stop-indicator-color — #ffffff
--tanrei-comp-slider-standard-focused-active-track-color — #1b1b1b
--tanrei-comp-slider-standard-focused-container-color — #1b1b1b · 10%
--tanrei-comp-slider-standard-focused-handle-outline-color — #1b1b1b
--tanrei-comp-slider-standard-focused-handle-color — #ffffff
--tanrei-comp-slider-standard-focused-handle-elevation —
--tanrei-comp-slider-standard-focused-handle-width — 20px
--tanrei-comp-slider-standard-focused-state-layer-color — #1b1b1b
--tanrei-comp-slider-standard-focused-stop-indicator-color — #4b4b4b
--tanrei-comp-slider-standard-focused-track-color — #eeeeee
--tanrei-comp-slider-standard-handle-outline-color sys.color.primary #1b1b1b
--tanrei-comp-slider-standard-handle-color sys.color.surface #ffffff
--tanrei-comp-slider-standard-handle-elevation sys.elevation.level0
--tanrei-comp-slider-standard-handle-width — 20px
--tanrei-comp-slider-standard-hovered-active-stop-indicator-color — #ffffff
--tanrei-comp-slider-standard-hovered-active-track-color — #1b1b1b
--tanrei-comp-slider-standard-hovered-container-color — #1b1b1b · 8%
--tanrei-comp-slider-standard-hovered-handle-outline-color — #1b1b1b
--tanrei-comp-slider-standard-hovered-handle-color — #ffffff
--tanrei-comp-slider-standard-hovered-handle-elevation —
--tanrei-comp-slider-standard-hovered-handle-width — 20px
--tanrei-comp-slider-standard-hovered-state-layer-color — #1b1b1b
--tanrei-comp-slider-standard-hovered-stop-indicator-color — #4b4b4b
--tanrei-comp-slider-standard-hovered-track-color — #eeeeee
--tanrei-comp-slider-standard-pressed-active-stop-indicator-color — #ffffff
--tanrei-comp-slider-standard-pressed-active-track-color — #1b1b1b
--tanrei-comp-slider-standard-pressed-container-color — #1b1b1b · 10%
--tanrei-comp-slider-standard-pressed-handle-outline-color — #1b1b1b
--tanrei-comp-slider-standard-pressed-handle-color — #ffffff
--tanrei-comp-slider-standard-pressed-handle-elevation —
--tanrei-comp-slider-standard-pressed-handle-width — 20px
--tanrei-comp-slider-standard-pressed-state-layer-color — #1b1b1b
--tanrei-comp-slider-standard-pressed-stop-indicator-color — #4b4b4b
--tanrei-comp-slider-standard-pressed-track-color — #eeeeee
--tanrei-comp-slider-standard-state-layer-color comp.slider.standard.handle-outline.color #1b1b1b
--tanrei-comp-slider-standard-stop-indicator-color sys.color.on-surface-variant #4b4b4b
--tanrei-comp-slider-standard-track-color sys.color.surface-variant #eeeeee
--tanrei-comp-slider-stop-indicator-size-large — 4px
--tanrei-comp-slider-stop-indicator-size-medium — 3px
--tanrei-comp-slider-stop-indicator-size-small — 2px
--tanrei-comp-slider-track-gap sys.space.space0 0px
--tanrei-comp-slider-track-shape-large sys.shape.corner.full 9999px
--tanrei-comp-slider-track-shape-medium sys.shape.corner.full 9999px
--tanrei-comp-slider-track-shape-small sys.shape.corner.full 9999px
--tanrei-comp-slider-track-thickness-large — 8px
--tanrei-comp-slider-track-thickness-medium — 6px
--tanrei-comp-slider-track-thickness-small — 4px
--tanrei-comp-slider-value-indicator-container-color sys.color.inverse-surface #1f1f1f
--tanrei-comp-slider-value-indicator-container-height — 24px
--tanrei-comp-slider-value-indicator-container-padding-inline sys.space.space100 8px
--tanrei-comp-slider-value-indicator-container-shape sys.shape.corner.small 6px
--tanrei-comp-slider-value-indicator-label-color sys.color.inverse-on-surface #f1f1f1
--tanrei-comp-slider-value-indicator-offset sys.space.space100 8px
--tanrei-comp-slider-value-indicator-label-typescale sys.typescale.label-small 500 12px/1.3333

测试样例

作为基准的渲染结果。所有平台都以相同的 ID 为每个样例截取快照,以便相互比较。

生成自 specs/slider.spec.json, tokens/comp/slider.tokens.json, design/tokens/

指南

何时使用

当用户凭感觉选择一个值,并在移动的同时看到它的效果时,使用滑块:音量、亮度、照片的曝光、地图的搜索半径。范围滑块适合有下限和上限的筛选条件,例如价格。

当确切的数字比它的位置更重要时,例如金额或数量,请使用文本字段,或者在滑块旁边放一个文本字段,让用户两种方式都能用。只有少数几个有名称的选项时,例如低、中、高,单选按钮可以说出每一个的名称;系统提供分段按钮的话,也可以用它。

连续还是步进

  • 连续(step 为 0)可以取 min 和 max 之间的任何值。中间的值有意义、效果又立即可见时,使用它。
  • 步进(step 大于 0)会吸附到 min 加上 step 的整数倍。只有整数单位才有意义时使用它:星级、以磅为单位的字号、以十为单位的百分比。
  • stops 在步进滑块可以取的每个值上画一个圆点。步数少时打开它,这样用户可以数出来。步数多时,圆点会挤满轨道,所以不要打开。

方向键每次移动一步;在连续滑块上,每次移动范围的百分之一。Page Up 和 Page Down 每次移动十步。

范围

range 绘制两个手柄,从 startValue 到 value。两个手柄可以相遇,但绝不交叉。每个手柄都是独立的 Tab 停靠点,对屏幕阅读器来说也是独立的控件,屏幕阅读器读到的是这个手柄还能到达的范围。两个手柄都要命名:startLabel(“最低价格”)和 label(“最高价格”)。

居中的滑块

origin 设置活动轨道从哪里开始。留空时,从 min 开始。设为范围之内的一个值,例如在 -50 到 50 的平衡控件上设为 0,活动轨道就从那里向任意一侧伸展。轨道的两端保留各自的刻度圆点,所以两个极限都始终可见。

数值指示器

valueIndicator 在手柄旁边的一个小标签里显示它的值。

值 何时显示数值 用途
none(默认) 从不 效果显示在屏幕上其他地方的滑块:音量、亮度
active 手柄被按下或获得键盘焦点时 用户在设置的同时需要读到的值,而手指会挡住手柄的场合
always 始终 必须一直可读的值,而滑块旁边又没有地方放你自己的文本时

指示器位于横向滑块的手柄上方,纵向滑块则位于行内起始侧。提供了 valueText 时,它显示 valueText;否则按平台的区域设置显示数字,小数位数与键盘步长的位数相同,最多两位。它只是值的一个视图:不获得焦点,也不接受输入,屏幕阅读器是从手柄读取值的。

值文本

当单独一个数字不够清楚时,把 valueText 设为描述当前值的文字:“40%”“3 星”“$300”。它会同时替换数值指示器里的数字和屏幕阅读器读出的内容。对于范围滑块,startValueText 对起始手柄起同样的作用。每当值变化时,都要更新它。

只写一次的文字,第一次拖动之后就不对了。所以滑块还接受一个把值变成文字的函数,只需给一次:每次变化时,它都会为每个手柄调用这个函数;在你设置了 valueText 和 startValueText 的地方,仍以它们为准。在 Web 上它是 formatValue,是属性(property),绝不是属性(attribute)。其他平台尚未实现。

const dollars = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
document.querySelector('[name="price"]').formatValue = (value) => dollars.format(value);

使用它的组件是滑块字段:字段和滑块合为一个组件,这些文字画在标签行的末尾。只要滑块需要画出来的标签、帮助文本或错误,就用它。

尺寸和方向

small 是日常使用的滑块。medium 和 large 给突出的控件一条更粗的轨道,例如媒体界面上的音量或亮度滑块。每种尺寸都满足最小目标。

无论布局方向如何,纵向滑块都是从底部(min)到顶部(max)。横向滑块从行内起始侧开始,所以在从右到左的布局中,min 在右边。

推荐

  • 把你自己的可见标签放在滑块旁边,用词与 label 相同。
  • 用户拖动时展示值的效果,并在 change 时保存,而不是在每次 input 时保存。
  • 选择合适的 min、max 和 step,让滑块能取的每个值都是用户可能想要的。
  • 数字本身说不清它计量的是什么时,给 valueText 加上单位。

避免

  • 不要把滑块用于用户必须精确设置的值,例如日期或一笔金额。
  • 不要把滑块用于只有开或关的设置。请使用开关。
  • 不要在步数很多的滑块上显示刻度圆点。
  • 不要把滑块放在工具栏里。它需要方向键,而工具栏正是用方向键移动焦点的。

无障碍

角色
slider
标注
label names the slider, or a range's end handle; startLabel names a range's start handle. Neither is drawn: a visible label is the product's text beside the slider, in the same words. valueText replaces the spoken number ("40 %", "3 stars"). The tracks, stop dots, state layer and value indicator are decorative.
最小目标
即使外观尺寸更小,点击区域也满足最小目标尺寸。 minimum 32px · pointer 44px · spacing 8px · touch 48px

键盘操作

按键 动作
Tab / Shift+Tab Moves focus to the handle, and for a range to the start handle, then the end handle.
Right Arrow / Up Arrow Increases the focused handle's value by one keyboard step (Right Arrow decreases it in a right-to-left horizontal slider).
Left Arrow / Down Arrow Decreases the value by one keyboard step (Left Arrow increases it in a right-to-left horizontal slider).
Page Up / Page Down Increases or decreases the value by ten keyboard steps.
Home / End Sets the value to the lowest or highest the handle can take.

备注

  • Each handle is one slider for assistive technology: role slider with aria-valuemin, aria-valuemax, aria-valuenow, aria-valuetext and aria-orientation on the web; the adjustable trait with an accessibility value on Apple platforms (a vertical swipe increments and decrements); a range with a current value and the set-progress action on Android; a slider semantics node with increase and decrease actions in Flutter; role adjustable with accessibilityValue and increment and decrement actions in React Native.
  • A range is two sliders, the WAI-ARIA multi-thumb pattern: each handle's lowest and highest value is bounded by the other handle, so a screen reader announces the range each can still reach.
  • The value is read from the handle and the active track. The handle must be told apart from each thing it lies on — the track, the active track and the surface behind the slider — by its outline (comp.slider.standard.handle-outline.color, where comp.slider.handle-outline.width is above 0) or by its fill (comp.slider.standard.handle.color), at 3:1 or more; comp.slider.standard.active-track.color must stay at least 3:1 against the track and the surface. In the default system the outline carries it against the track and the surface, and the fill against the active track. A faint track keeps its end visible by the stop dot at the end of the travel (SLD-9).
  • The handle may be drawn smaller than the minimum target (20 against 48 in the default system): the whole slider takes the press across its axis at the target's size (SLD-7), so every handle keeps its whole target, a range's two handles included, and no target is reported larger than the area that takes the press.
  • Text size does not change the slider: it has no text of its own except the value indicator, which grows with the text.
  • How a state looks is a value decision of the active design system; components read the state's tokens and never compute the effect themselves.

生成自 specs/slider.spec.json, tokens/sys/measure.tokens.json

组件负责的部分

  • 对辅助技术来说,每个手柄都是一个滑块,由 label(范围滑块的起始手柄则由 startLabel)命名,带有它的值、值文本、它能取的最小值和最大值、方向和禁用状态。在 Apple 的平台上,它是一个可调节元素:纵向轻扫使它移动一步。
  • 范围滑块是两个滑块,即 WAI-ARIA 的多手柄(multi-thumb)模式。每个手柄的极限受另一个手柄约束,所以屏幕阅读器朗读的是它还能到达的范围。
  • 轨道、刻度圆点、状态层和数值指示器对辅助技术隐藏。
  • 每个手柄都是一个 Tab 停靠点,起始手柄在前。方向键移动一步,Page Up 和 Page Down 移动十步,Home 和 End 移到最小值和最大值。在从右到左的横向滑块中,右方向键和左方向键互换。
  • 屏幕阅读器的增加和减少操作移动一步;在平台允许的地方直接设置值时,值会被限制在范围之内并吸附到步进上。
  • 在滑块的任意位置按下,都会把较近的手柄移到那里,手柄随后跟随拖动。触摸时,如果按下之后先是横穿轨道移动,这次按下就留给包裹滑块的滚动视图,所以滑块不会截住滚动。
  • 整个滑块都是点击区域,并且在垂直于轨道的方向上达到最小目标。手柄画得比目标小,但这并不会缩小点击区域,所以范围滑块的每个手柄也都保有完整的目标。
  • 它只在键盘聚焦时,围绕获得焦点的手柄显示焦点指示。
  • 在默认系统中,手柄与它下方的一切都以 3:1 或更高的对比度区分开来:靠轮廓区别于轨道和页面,靠填充区别于活动轨道。活动轨道相对于轨道和页面保持 3:1,颜色很淡的轨道的末端则靠刻度圆点保持可见。
  • 禁用的滑块不可聚焦,不显示数值指示器,并且暴露为禁用。
  • 文本大小只改变数值指示器,它随文本一起变大。
  • 调试构建会报告:缺少 label 或 startLabel;值、起始值或原点超出范围或不在步进上;起始值大于值;max 不大于 min;步长为负;以及未知的尺寸、方向或数值指示器设置。

已知的平台差距:

  • 在 Web 上,每个手柄的滑块角色设在 shadow root 里,而不是通过 ElementInternals,因为 axe-core 看不到以那种方式设置的语义。它不是 <input type="range">:后者只有一个手柄,而且它的部件在任何两个引擎里的样式设置方式都不一样。
  • Web:辅助技术是通过浏览器发给 ARIA 滑块的方向键来调节它的,所以无法直接设置值。
  • Web:数值指示器不在顶层(top layer)。带有 overflow: hidden 的祖先元素会裁剪它,后面一个 z-index 大于 1 的定位兄弟元素会盖住它。
  • SwiftUI 无法暴露手柄的最小值和最大值,也无法暴露它的方向;在 macOS 上,手柄对辅助技术来说不是滑块。
  • SwiftUI 无法区分触摸和指针:在 iOS 上,每次按下都算触摸,iPad 的指针也是;在 macOS 上,每次按下都算指针。在 iOS 18 之前,触摸的拖动是一个 SwiftUI 手势,包裹滑块的滚动视图可能无法接管它。
  • SwiftUI:数值指示器被限制在滑块之内,而不是窗口之内,在滑块之后绘制的视图可能盖住它。
  • SwiftUI:按键是按规则测试的,还没有用真实的键盘测试,因为模拟器在自动化下不提供键盘焦点。
  • Compose 的滑块语义里没有方向,所以手柄的方向没有暴露。
  • Flutter 不暴露手柄的最小值、最大值和方向,也没有直接设置值的操作。
  • 在任何区域设置下,Flutter 都始终用 . 作为小数分隔符来书写数字。在这一点有影响的地方,请设置 valueText。
  • Flutter:只有当滑块的父级比滑块大时,点击区域才会在垂直于轨道的方向上扩大。与滑块大小完全相同的父级会把它截掉。
  • React Native 没有直接设置值的操作,也没有方向属性,所以两者都没有暴露。
  • 在 React Native 上,方向键只在连接了硬件键盘时有效,与标签页那一行的情况相同。
  • React Native 把每次按下都当作触摸:鼠标或触控笔的按下要等到它移动或结束之后,才会移动手柄。
  • React Native 把数值指示器画在滑块之内,而不是浮层里,所以它被限制在滑块之内,而不是窗口之内,在滑块之后绘制的视图可能盖住它。
  • React Native:布局、拖动及其让位给滚动视图的方式,以及可调节的手柄,都还没有在真机上验证过。
  • SwiftUI、Compose、Flutter 和 React Native 仍然绘制以前的外观:实心的手柄,所有尺寸都用同一种大小的刻度圆点,轨道在手柄处截断,而不是在它下方相接。在截断的地方,它们收尾的方式各不相同:SwiftUI 是平头,Flutter 和 React Native 是带缺口的圆头,Compose 则按手柄的形状截断。
  • 在 cupertino 下,浅色配色方案中白色手柄放在白色页面上的对比度为 1.0:1,低于显示数值的部件所需的 3:1。它按 Apple 的画法没有轮廓,靠阴影和旁边着色的活动轨道来区分;没有任何不变式检查手柄相对于页面的对比度。这个 1.0:1 是作为参考信息保留的测量值:Tanrei 的无障碍准则不适用于模仿预设。在 cupertino 下,滑块也不绘制刻度圆点,连轨道两端的也没有。
  • 在 m3-like 下,滑块按 Material 公布的尺寸绘制 Material 的竖条。这些尺寸还没有对照保真度参考验证过。在 m3-expressive-like 下,Material 画在粗轨道里的图标没有位置可放(模仿差距 slider-inset-icon):请把你的图标放在滑块旁边。

需要你做的事

  • 给每个滑块一个 label,范围滑块还要有 startLabel,使用你的产品所用的语言。把同样的文字作为可见标签显示在它旁边。
  • 数字本身不够清楚时设置 valueText,并让它与值保持同步。
  • 把变化的结果显示在用户看得到的地方;如果某次变化无法应用,用文本告诉用户。滑块只朗读它自己的值。
  • 在 cupertino 下,手柄是白色的,轨道末端也没有圆点,所以只要可以,就不要把滑块放在纯白的表面上。
  • 在最高容器色调的表面上(例如填充卡片),非活动轨道几乎是同一色调,很难看清。滑块放在这样的表面上时,请在图层中把 comp.slider.standard.track.color 指向更强的角色。手柄和活动轨道仍然显示数值。
  • 在 Web 上,给滑块一个 name,它的值才会随表单提交。