slider —— Deslizador
El deslizador convierte la selección numérica en una operación espacial: arrastrar es más intuitivo que teclear y permite ver de un vistazo dónde se sitúa el valor dentro del rango. core/slider gestiona la conversión bidireccional «valor ↔ posición», el ajuste a pasos (snap), el valor inicial de los dos puntos del modo intervalo (range) y la semántica slider de APG.
Cuándo usarlo
Sección titulada «Cuándo usarlo»- Todos los extremos implementan
LhSlider: volumen, rango de precios, ajuste de opacidad. - Escenarios con valores continuos o una granularidad de paso gruesa.
- No usarlo: entrada de valores numéricos precisos (usa
input-number; un deslizador no puede expresar un valor no múltiplo del paso como «37»).
Importación
Sección titulada «Importación»import { snapToStep, clampAndSnap, getValueFromPosition, getPositionFromValue, getSliderClassNames, getInitialValue, buildMarksList, getSliderThumbAria,} from '@lh-ui/share/core/slider'import type { LhSliderState, LhSliderMark } from '@lh-ui/share/core/slider'Intención de diseño
Sección titulada «Intención de diseño»snapToStep devuelve el valor original cuando step no es finito o es <= 0, en lugar de devolver NaN — step: 0 produciría NaN en la operación de módulo; aquí se trata el «paso no válido» como «sin ajuste», y el clamp puro de límites se deja a clampAndSnap. Separación de responsabilidades: el ajuste y el clamp son dos cosas independientes.
Que getPositionFromValue devuelva 0 cuando max === min evita la división por cero: si el rango degenera en un punto, el deslizador naturalmente debe estar en el extremo inicial.
getInitialValue devuelve [clamp a min, clamp a (min+max)/2] en el modo intervalo — el segundo deslizador se sitúa por defecto en el punto medio y no en max, por razones visuales (dos deslizadores pegados parecen un bug).
Tabla de funciones
Sección titulada «Tabla de funciones»| Función | Firma | Devuelve | Función |
|---|---|---|---|
snapToStep |
(value: number, min: number, step: number) => number |
valor ajustado | alinea al punto de la rejilla más cercano según step |
clampAndSnap |
(value: number, min: number, max: number, step: number) => number |
valor normalizado | primero el ajuste, luego el clamp |
getValueFromPosition |
(pct: number, min: number, max: number, step: number) => number |
valor numérico | porcentaje → valor |
getPositionFromValue |
(value: number, min: number, max: number) => number |
porcentaje [0,1] | valor → posición |
getSliderClassNames |
(state: LhSliderState) => string[] |
clases | disabled/range/size |
getInitialValue |
(state: LhSliderState) => number | [number, number] |
valor inicial | el modo intervalo devuelve dos puntos |
buildMarksList |
(marks?: Record<number, string>) => LhSliderMark[] |
matriz de marcas | orden ascendente por value |
getSliderThumbAria |
(options: { value, min, max, vertical?, disabled?, ariaValueText? }) => LhAriaAttrs |
bolsa de atributos aria | semántica slider |
Definiciones de tipos
Sección titulada «Definiciones de tipos»export interface LhSliderMark { label: string value: number}
export interface LhSliderState { value: number | [number, number] | null min: number max: number step: number disabled: boolean range: boolean // modo intervalo (dos deslizadores) showInput: boolean // acoplar con input-number size: LhComponentSize marks: Record<number, string> | undefined}Accesibilidad
Sección titulada «Accesibilidad»getSliderThumbAria devuelve la semántica slider de APG. En el modo intervalo debe llamarse una vez para cada deslizador:
getSliderThumbAria({ value: 30, min: 0, max: 100 })// { role: 'slider', 'aria-valuenow': 30, 'aria-valuemin': 0, 'aria-valuemax': 100 }
getSliderThumbAria({ value: 60, min: 0, max: 100, vertical: true, disabled: true })// { role: 'slider', 'aria-valuenow': 60, 'aria-valuemin': 0, 'aria-valuemax': 100,// 'aria-orientation': 'vertical', 'aria-disabled': true }verticalproducearia-orientation: 'vertical'(la horizontal es la dirección por defecto y no se declara).ariaValueTextproducearia-valuetext— se usa cuando el valor necesita una expresión semántica (como «30 %» o «lento») y se anuncia con prioridad sobre valuenow.- Interacción de teclado (capa UI): ←/→ o ↓/↑ aumentan/disminuyen según step; Home/End saltan a min/max; PageUp/Down dan pasos grandes.
Ejemplos de uso
Sección titulada «Ejemplos de uso»import { getValueFromPosition, getPositionFromValue, snapToStep } from '@lh-ui/share/core/slider'
getValueFromPosition(0.5, 0, 100, 1) // 50getValueFromPosition(0.333, 0, 100, 5) // 35 (ajustado a múltiplo de 5)getPositionFromValue(30, 0, 100) // 0.3getPositionFromValue(150, 0, 100) // 1 (con clamp)getPositionFromValue(50, 50, 50) // 0 (max===min, evita la división por cero)snapToStep(7, 0, 5) // 5snapToStep(8, 0, 5) // 10snapToStep(7, 0, 0) // 7 (step no válido, sin ajuste)buildMarksList({ 0: 'Bajo', 100: 'Alto' }) // [{ label: 'Bajo', value: 0 }, { label: 'Alto', value: 100 }]<!-- Vue3 --><div :class="getSliderClassNames({ value, min, max, step, disabled, range, showInput, size, marks })"> <div class="lh-slider__track"> <div class="lh-slider__bar" :style="{ width: pct + '%' }" /> <div class="lh-slider__button-wrapper" :style="{ left: pct + '%' }" v-bind="getSliderThumbAria({ value, min, max, disabled })" @pointerdown="startDrag" > <div class="lh-slider__button" /> </div> <template v-if="marks"> <div v-for="mark in buildMarksList(marks)" :key="mark.value" class="lh-slider__marks"> <span class="lh-slider__mark" :style="{ left: getPositionFromValue(mark.value, min, max) * 100 + '%' }"> {{ mark.label }} </span> </div> </template> </div></div>// React: el arrastre usa pointer events + getBoundingClientRect para calcular el porcentaje// const pct = (clientX - rect.left) / rect.width// onChange(getValueFromPosition(pct, min, max, step))// uniapp: <slider> es un componente integrado (deslizador único), soporta min/max/step/show-value;// el modo intervalo y las marks necesitan una implementación propia con view,// el arrastre usa @touchmoveEstilos y tokens
Sección titulada «Estilos y tokens»| Clase | Semántica |
|---|---|
.lh-slider |
clase base |
.lh-slider--{small|large} |
tamaño (tamaño del deslizador) |
.lh-slider--disabled |
deshabilitado |
.lh-slider--range |
modo intervalo (dos deslizadores + tramo central resaltado) |
.lh-slider__track |
pista |
.lh-slider__bar |
intervalo seleccionado (tramo resaltado) |
.lh-slider__button-wrapper / __button |
deslizador (pulgar) |
.lh-slider__marks / __mark |
marcas |
.lh-slider__input / __input-text |
campo de entrada acoplado |
Color de la pista --lh-fill-color, tramo seleccionado --lh-primary-color.
Advertencias
Sección titulada «Advertencias»getPositionFromValuedevuelve un decimal en [0,1]; la capa UI añade el%; no creas que devuelve el porcentaje.- Alineación del ajuste y las marcas: el value de
marksdebería ser múltiplo de step; de lo contrario la marca caerá entre dos valores ajustados y el usuario no podrá arrastrarla hasta allí. - Cruce del modo intervalo: los dos deslizadores pueden cruzarse entre sí (la capa UI normalmente lo prohíbe o los intercambia automáticamente); el
clampAndSnapdel core no gestiona esta semántica. - Modo vertical: con
vertical, la altura de la pista sustituye a la anchura y el denominador de la conversión debe cambiar; elgetValueFromPositiondel core es independiente del eje (toma el porcentaje); la capa UI calcula el pct. - Acoplamiento de
showInput: el min/max/step deinput-numberdebería coincidir con el del slider; de lo contrario los rangos de valores de ambos serán inconsistentes.
@lh-ui/share/core/slider → getValueFromPosition( )(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue