slider —— 슬라이더
슬라이더는 숫자 선택을 공간적 조작으로 바꿉니다: 입력하는 것보다 끌어당기는 것이 더 직관적이며, 값이 범위 안에서 어디 있는지 한눈에 볼 수 있습니다. core/slider가 관리하는 것은 “값 ↔ 위치”의 양방향 환산, 스텝 흡착, 구간 (range) 모드의 두 점 초깃값, 그리고 APG slider 의미입니다.
언제 사용할까
섹션 제목: “언제 사용할까”- 각 말단 구현
LhSlider: 음량, 가격 범위, 불투명도 조절. - 값 범위가 연속적이거나 스텝 단위가 비교적 굵은 상황.
- 사용하면 안 되는 경우: 정밀한 숫자 입력 (
input-number사용, 슬라이더는 “37” 같은 스텝이 아닌 값을 표현할 수 없음).
임포트 방법
섹션 제목: “임포트 방법”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'설계 의도
섹션 제목: “설계 의도”snapToStep는 step이 유한하지 않거나 <= 0일 때 NaN을 반환하는 대신 원래 값을 반환합니다——step: 0은 모듈러 연산에서 NaN을 만들어내며, 여기서는 “잘못된 스텝”을 “흡착 없음”으로 취급하고, 순수한 경계 클램프는 clampAndSnap에 맡깁니다. 역할의 분리: 흡착과 클램프는 독립적인 두 가지 일입니다.
getPositionFromValue가 max === min일 때 0을 반환하는 것은 0으로 나누기를 막기 위함입니다: 값 범위가 한 점으로 퇴화하면 슬라이더는 당연히 시작점에 있어야 합니다.
getInitialValue는 구간 모드에서 [min 클램프, (min+max)/2 클램프]를 반환합니다——두 번째 슬라이더의 기본 위치가 max가 아니라 중간점인 것은 시각적 고려입니다 (두 슬라이더가 붙어 있으면 버그처럼 보입니다).
함수 표
섹션 제목: “함수 표”| 함수 | 시그니처 | 반환 | 역할 |
|---|---|---|---|
snapToStep |
(value: number, min: number, step: number) => number |
흡착값 | step에 가장 가까운 격자점으로 정렬 |
clampAndSnap |
(value: number, min: number, max: number, step: number) => number |
정규화값 | 먼저 흡착 후 클램프 |
getValueFromPosition |
(pct: number, min: number, max: number, step: number) => number |
숫자 | 백분율 → 값 |
getPositionFromValue |
(value: number, min: number, max: number) => number |
백분율 [0,1] | 값 → 위치 |
getSliderClassNames |
(state: LhSliderState) => string[] |
클래스명 | disabled/range/size |
getInitialValue |
(state: LhSliderState) => number | [number, number] |
초깃값 | 구간은 두 점 반환 |
buildMarksList |
(marks?: Record<number, string>) => LhSliderMark[] |
눈금 배열 | value 오름차순 |
getSliderThumbAria |
(options: { value, min, max, vertical?, disabled?, ariaValueText? }) => LhAriaAttrs |
aria 속성 팩 | slider 의미 |
타입 정의
섹션 제목: “타입 정의”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 // 구간 모드 (두 개의 슬라이더) showInput: boolean // input-number 연동 size: LhComponentSize marks: Record<number, string> | undefined}접근성
섹션 제목: “접근성”getSliderThumbAria는 APG slider 의미를 출력합니다. 구간 모드에서는 두 슬라이더에 각각 한 번씩 호출해야 합니다:
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 }vertical은aria-orientation: 'vertical'을 출력합니다 (수평은 기본 방향이므로 선언하지 않음).ariaValueText는aria-valuetext를 출력합니다——숫자가 의미적으로 표현되어야 할 때 사용하며 (예: “30%” 또는 “느리게”), valuenow보다 우선하여 읽힙니다.- 키보드 상호작용 (UI 계층): ←/→ 또는 ↓/↑로 step 단위 증감, Home/End로 min/max로 이동, PageUp/Down으로 큰 스텝.
사용 예시
섹션 제목: “사용 예시”import { getValueFromPosition, getPositionFromValue, snapToStep } from '@lh-ui/share/core/slider'
getValueFromPosition(0.5, 0, 100, 1) // 50getValueFromPosition(0.333, 0, 100, 5) // 35 (5의 배수로 흡착)getPositionFromValue(30, 0, 100) // 0.3getPositionFromValue(150, 0, 100) // 1 (클램프)getPositionFromValue(50, 50, 50) // 0 (max===min, 0으로 나누기 방지)snapToStep(7, 0, 5) // 5snapToStep(8, 0, 5) // 10snapToStep(7, 0, 0) // 7 (잘못된 step은 흡착하지 않음)buildMarksList({ 0: '낮음', 100: '높음' }) // [{ label: '낮음', value: 0 }, { label: '높음', 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: 드래그는 pointer events + getBoundingClientRect로 백분율 계산// const pct = (clientX - rect.left) / rect.width// onChange(getValueFromPosition(pct, min, max, step))// uniapp: <slider>는 내장 컴포넌트 (단일 슬라이더), min/max/step/show-value 지원;// 구간 모드와 marks는 커스텀 view로 구현해야 하며 드래그는 @touchmove 사용스타일과 토큰
섹션 제목: “스타일과 토큰”| 클래스명 | 의미 |
|---|---|
.lh-slider |
기본 클래스 |
.lh-slider--{small|large} |
크기 단계 (슬라이더 크기) |
.lh-slider--disabled |
비활성 |
.lh-slider--range |
구간 모드 (두 개의 슬라이더 + 중간 하이라이트 구간) |
.lh-slider__track |
트랙 |
.lh-slider__bar |
선택 구간 (하이라이트 구간) |
.lh-slider__button-wrapper / __button |
슬라이더 손잡이 |
.lh-slider__marks / __mark |
눈금 |
.lh-slider__input / __input-text |
연동 입력란 |
트랙 색은 --lh-fill-color, 선택 구간은 --lh-primary-color입니다.
주의사항
섹션 제목: “주의사항”getPositionFromValue는 [0,1]의 소수를 반환, UI 계층이%를 붙입니다; 백분율을 반환한다고 오해하지 마세요.- 흡착과 눈금의 정렬:
marks의 value는 step의 배수여야 하며, 그렇지 않으면 눈금점이 두 흡착 값 사이에 떨어져 사용자가 거기까지 끌어당길 수 없습니다. - 구간 모드의 교차: 두 슬라이더는 서로를 통과할 수 있습니다 (UI 계층은 보통 금지하거나 자동으로 교환), core의
clampAndSnap은 이 의미를 처리하지 않습니다. - 수직 모드:
vertical일 때는 트랙 높이가 너비를 대신하고 환산의 분모가 바뀝니다; core의getValueFromPosition은 축에 무관하여 (백분율을 받음) pct를 계산하는 것은 UI 계층의 책임입니다. showInput의 연동:input-number의 min/max/step은 slider와 일치해야 하며, 그렇지 않으면 양쪽의 값 범위가 달라집니다.
@lh-ui/share/core/slider → getValueFromPosition( )(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue