Slider 滑块

在数值范围内拖动取值。

'use client'

import { useState } from 'react'
import { Slider } from '@hina-ui/react'

export default function Demo() {
  const [volume, setVolume] = useState<number | undefined>(60)

  return <Slider value={volume} onValueChange={setVolume} aria-label="音量" className="w-64" />
}
tsx

用法

import { Slider } from '@hina-ui/react'
ts

滑块在 min 与 max 之间取一个数,value / onValueChange 绑定当前值。拖动过程中值持续更新,松手时另外调用一次 onCommit。未声明的属性都会传给拇指元素,应当用 aria-label 或者 aria-labelledby 命名。宽度归布局,滑块本身撑满容器。

当前值:40

'use client'

import { useState } from 'react'
import { Slider, Stack, Text } from '@hina-ui/react'

export default function Demo() {
  const [brightness, setBrightness] = useState<number | undefined>(40)

  return (
    <Stack gap="sm" className="w-64">
      <Slider value={brightness} onValueChange={setBrightness} aria-label="亮度" />
      <Text size="sm" tone="muted">
        当前值:{brightness}
      </Text>
    </Stack>
  )
}
tsx

示例

范围与步长

min、max 与 step 决定取值范围与粒度,键盘方向键也按 step 移动。

'use client'

import { useState } from 'react'
import { Slider } from '@hina-ui/react'

export default function Demo() {
  const [rating, setRating] = useState<number | undefined>(3.5)

  return (
    <Slider
      value={rating}
      onValueChange={setRating}
      min={1}
      max={5}
      step={0.5}
      aria-label="评分"
      className="w-64"
    />
  )
}
tsx

方向

dir 支持 ltr 和 rtl,优先于 ConfigProvider 的全局方向配置;未配置时继承外层元素的 dir。切换方向会同步更新轨道、拇指、刻度点与刻度文字。

RTL 下最小值在右、最大值在左;右方向键减小数值,左方向键增大数值,Home / End 仍分别跳到最小值与最大值。

LTR

RTL

'use client'

import { useState } from 'react'
import { Slider, Stack, Text } from '@hina-ui/react'

const marks = [0, 25, 50, 75, 100].map(value => ({ value, label: String(value) }))

export default function Demo() {
  const [value, setValue] = useState<number | undefined>(25)

  return (
    <Stack gap="lg" className="w-72 max-w-full">
      {(['ltr', 'rtl'] as const).map(dir => (
        <Stack key={dir} gap="xs">
          <Text size="sm" tone="muted">
            {dir.toUpperCase()}
          </Text>
          <Slider
            value={value}
            onValueChange={setValue}
            dir={dir}
            marks={marks}
            step={5}
            aria-label={'数值 (' + dir.toUpperCase() + ')'}
          />
        </Stack>
      ))}
    </Stack>
  )
}
tsx

刻度

marks 在轨道上放刻度点,带 label 的刻度在下方显示文字。

'use client'

import { useState } from 'react'
import { Slider } from '@hina-ui/react'

const marks = [
  { value: 0, label: '慢' },
  { value: 25 },
  { value: 50, label: '中' },
  { value: 75 },
  { value: 100, label: '快' },
]

export default function Demo() {
  const [speed, setSpeed] = useState<number | undefined>(50)

  return (
    <Slider
      value={speed}
      onValueChange={setSpeed}
      step={25}
      marks={marks}
      aria-label="翻页速度"
      className="w-64"
    />
  )
}
tsx

取值标签

取值标签是一个 Tooltip,以拇指为触发器,随拇指移动,接近视口边缘时会翻转到另一侧。默认在悬停、聚焦与拖动时显示当前值;label 为 always 时始终显示,为 none 时不显示。format 定制显示的文字,默认按当前语言格式化数字。与 IconButton 的提示相同,它依赖应用中的 TooltipProvider,缺少时不显示。

'use client'

import { useState } from 'react'
import { Slider, Stack } from '@hina-ui/react'

export default function Demo() {
  const [quality, setQuality] = useState<number | undefined>(80)
  const [opacity, setOpacity] = useState<number | undefined>(30)

  return (
    <Stack gap="lg" className="w-64 pt-6">
      <Slider
        value={quality}
        onValueChange={setQuality}
        label="always"
        format={(v: number) => `${v}%`}
        aria-label="图片质量"
      />
      <Slider value={opacity} onValueChange={setOpacity} label="none" aria-label="遮罩透明度" />
    </Stack>
  )
}
tsx

尺寸

size 有 sm、md、lg 三档,轨道高 20、24、28 像素,拇指 12、16、20 像素,与 Switch 相同。

import { Slider, Stack } from '@hina-ui/react'

export default function Demo() {
  return (
    <Stack gap="sm" className="w-64">
      <Slider size="sm" value={30} aria-label="小号" />
      <Slider size="md" value={50} aria-label="中号" />
      <Slider size="lg" value={70} aria-label="大号" />
    </Stack>
  )
}
tsx

状态

disabled 禁用整个滑块。

import { Slider } from '@hina-ui/react'

export default function Demo() {
  return <Slider disabled value={45} aria-label="已禁用" className="w-64" />
}
tsx

松手时提交

onCommit 只在一次拖动或者一次按键结束时调用,适合发起请求之类代价较高的操作。

拖动中:20,松手后:20

'use client'

import { useState } from 'react'
import { Slider, Stack, Text } from '@hina-ui/react'

export default function Demo() {
  const [value, setValue] = useState<number | undefined>(20)
  const [committed, setCommitted] = useState(20)

  return (
    <Stack gap="sm" className="w-64">
      <Slider value={value} onValueChange={setValue} aria-label="阈值" onCommit={setCommitted} />
      <Text size="sm" tone="muted">
        拖动中:{value},松手后:{committed}
      </Text>
    </Stack>
  )
}
tsx

在表单中

放进 FormField 后,标签通过 aria-labelledby 关联到拇指,说明与错误信息由字段渲染;校验规则与提交交给 Form。

60 以下的画质会明显失真

'use client'

import { useState } from 'react'
import * as v from 'valibot'
import { Button, Form, FormField, Slider, Text } from '@hina-ui/react'

const schema = v.object({
  quality: v.pipe(v.number(), v.minValue(60, '画质不能低于 60')),
})

export default function Demo() {
  const [values, setValues] = useState({ quality: 40 })
  const [saved, setSaved] = useState('')

  async function save(data: unknown) {
    await new Promise(resolve => setTimeout(resolve, 600))
    setSaved(JSON.stringify(data))
  }

  return (
    <Form values={values} rules={schema} className="w-80" onSubmit={save}>
      {({ submitting }) => (
        <>
          <FormField name="quality" label="导出画质" description="60 以下的画质会明显失真">
            <Slider
              value={values.quality}
              onValueChange={quality => setValues({ ...values, quality: quality ?? 0 })}
              min={0}
              max={100}
              step={5}
            />
          </FormField>
          <Button type="submit" loading={submitting} className="self-start">
            导出
          </Button>
          {saved && (
            <Text tone="muted" size="sm">
              已导出:{saved}
            </Text>
          )}
        </>
      )}
    </Form>
  )
}
tsx

行为

  • 点击轨道任意位置,拇指跳到该处并开始拖动;拖动时拇指与填充跟手,不带过渡;点击与键盘引起的跳动带过渡。
  • 键盘 Tab 落在拇指上,左右方向键按 step 移动,PageUp / PageDown 大步移动,Home / End 跳到两端。
  • 悬停整条滑块时拇指落墨、取值标签淡入;按住拖动时加深。

无障碍

  • 拇指是 role="slider",带 aria-valuenow、aria-valuemin、aria-valuemax 与 aria-orientation。
  • 通过 aria-label 或者 aria-labelledby 给拇指命名;刻度对屏幕阅读器隐藏,屏幕阅读器读取的是 aria-valuenow;取值标签显示时作为拇指的 aria-describedby。

API

Props

属性
类型
默认值
说明
value
number
—
当前值,未绑定时落在 min
min
number
0
最小值
max
number
100
最大值
step
number
1
步长
dir
'ltr' | 'rtl'
—
方向,未指定时继承
marks
Array<{ value: number; label?: string }>
—
刻度
label
'auto' | 'always' | 'none'
'auto'
取值标签的显示方式
format
(value: number) => string
—
取值标签的文字,默认按语言格式化
size
'sm' | 'md' | 'lg'
'md'
尺寸
disabled
boolean
false
是否禁用
className
string
—
追加至根元素的类名

回调

回调
参数
说明
onValueChange
value: number
值变化时,拖动中持续触发
onCommit
value: number
一次拖动或者按键结束时