# Slider 滑块

在连续区间内取值，比数字输入框更直观。支持横向与纵向两种布局，拖动过程通过 `requestAnimationFrame` 合帧（每帧最多抛一次），松手或键盘调节时才提交最终值。轨道粗细与拇指直径既可由 `size` 预设整体缩放，也能用 `trackSize` / `thumbSize` 单独覆盖。

## 代码演示

### 基础用法

`v-model` 绑定数值，`min` / `max` / `step` 描述取值范围。拖动过程中 `update:modelValue` 持续更新，松手时抛一次 `change`。

**演示说明：** `change` 的参数是拖动结束时的最终值。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const value = ref(40)
const committed = ref(40)

function onChange(next: number) {
  committed.value = next
}
</script>

<template>
  <div class="m-row-demo--stretch">
    <MSlider v-model="value" :min="0" :max="100" :step="1" @change="onChange" />
    <span>拖动中：{{ value }}，松手后（change）：{{ committed }}</span>
  </div>
</template>
```

### 纵向布局

`vertical` 后轨道自上而下、最小值在底部。组件自身高度是 `100%`，**必须由父容器给出高度**，否则会塌陷。

**演示说明：** 父容器 `height: 160px`，三个纵向滑块共享同一高度。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const volume = ref(70)
const brightness = ref(45)
const zoom = ref(20)
</script>

<template>
  <div class="m-row-demo" style="align-items: flex-end">
    <div style="height: 160px; display: flex; gap: 28px">
      <MSlider v-model="volume" vertical color="primary" />
      <MSlider v-model="brightness" vertical color="warning" />
      <MSlider v-model="zoom" vertical color="info" />
    </div>
    <span>音量 {{ volume }} / 亮度 {{ brightness }} / 缩放 {{ zoom }}</span>
  </div>
</template>
```

### 尺寸与尺寸覆盖

`size` 三档整体等比缩放（`small` 0.75×、`large` 1.5×）。`trackSize` / `thumbSize` 以 px 覆盖对应部分，优先级高于 `size`。

**演示说明：** 后两条分别只改轨道（细线型）和只改拇指（大触摸目标）。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const small = ref(30)
const normal = ref(50)
const large = ref(70)
const thinRail = ref(60)
const fatThumb = ref(40)
</script>

<template>
  <div class="m-row-demo--stretch">
    <MSlider v-model="small" size="small" />
    <MSlider v-model="normal" size="default" />
    <MSlider v-model="large" size="large" />
    <MSlider v-model="thinRail" :track-size="2" :thumb-size="10" />
    <MSlider v-model="fatThumb" :track-size="10" :thumb-size="28" />
  </div>
</template>
```

### 语义色

六种语义色对应当前值的填充色，与全局 `--m-*` 令牌联动。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const colors = ['primary', 'secondary', 'success', 'warning', 'error', 'info'] as const
const values = ref([20, 35, 50, 65, 80, 95])
</script>

<template>
  <div class="m-row-demo--stretch">
    <MSlider v-for="(color, index) in colors" :key="color" v-model="values[index]" :color="color" />
  </div>
</template>
```

### 禁用、气泡与步进

`disabled` 时不可交互；`showLabel="false"` 关闭拖动时跟随拇指的数值气泡；`step` 决定取值的量化粒度。

```vue
<script setup lang="ts">
import { ref } from 'vue'

const locked = ref(60)
const quiet = ref(50)
const stepped = ref(20)
</script>

<template>
  <div class="m-row-demo--stretch">
    <MSlider v-model="locked" disabled />
    <MSlider v-model="quiet" :show-label="false" />
    <MSlider v-model="stepped" :min="0" :max="100" :step="10" />
    <span>步进值（step = 10）：{{ stepped }}</span>
  </div>
</template>
```

## API

### Props

| 属性 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `modelValue` | `number` | `undefined`（内部按 `min` 处理） | 当前值（v-model） |
| `min` | `number` | `0` | 最小值 |
| `max` | `number` | `100` | 最大值 |
| `step` | `number` | `1` | 步进 |
| `disabled` | `boolean` | `false` | 是否禁用 |
| `showLabel` | `boolean` | `true` | 拖动 / 键盘调节时显示数值气泡 |
| `vertical` | `boolean` | `false` | 纵向布局：轨道自上而下，最小值在底部，需由父容器提供高度 |
| `color` | `'primary' \| 'secondary' \| 'success' \| 'warning' \| 'error' \| 'info'` | `'primary'` | 语义色变体 |
| `size` | `'small' \| 'default' \| 'large'` | `'default'` | 尺寸预设：同时缩放轨道粗细、拇指直径、描边与光圈 |
| `trackSize` | `number` | `undefined` | 轨道粗细 px：覆盖 `size` 预设的轨道值，优先级最高 |
| `thumbSize` | `number` | `undefined` | 拇指直径 px：覆盖 `size` 预设的拇指值，优先级最高 |

### Events

| 事件 | 参数 | 说明 |
| --- | --- | --- |
| `update:modelValue` | `value: number` | 值变化（拖动中每帧最多一次） |
| `change` | `value: number` | 拖动结束 / 键盘调节时触发 |

### Slots

无。

### 尺寸预设

`size` 以 `default`（拇指 16px / 轨道 4px）为基准等比缩放，`trackSize` / `thumbSize` 会覆盖对应部分。

| 取值 | 缩放 | 拇指直径 | 轨道粗细 |
| --- | --- | --- | --- |
| `small` | 0.75× | 12px | 3px |
| `default` | 1× | 16px | 4px |
| `large` | 1.5× | 24px | 6px |

## 使用建议

::: tip 纵向滑块要给容器高度
`vertical` 时滑块根元素高度是 `100%`，尺寸完全来自父容器。用一层定高（或 `flex: 1`）的容器包住它是最省事的做法。
:::

::: tip 拖动中不要做重活
`update:modelValue` 已经按帧合帧，但每一帧仍会触发一次响应式更新。若下游依赖很重，建议只在 `change` 里做真正的业务处理。
:::

::: warning trackSize / thumbSize 只接受有限正数
传入 `0`、负数或 `NaN` 会被忽略并回退到 `size` 预设值，不会把轨道或拇指变成不可见的尺寸。
:::

::: warning 点击轨道是跳转而非拖拽
点轨道会立即把值跳到该位置（带过渡），首次移动才进入拖拽态。需要「点击不改变值」的交互时要自行拦截。
:::

## 相关组件

- [InputNumber 数字输入框](/components/input-number)：需要精确输入或键盘长按步进时使用。
- [Progress 进度条](/components/progress)：只读展示进度、不接受输入时使用。
