--- title: SliderCaptcha 滑块验证码 description: 按住滑块拖到行内结束端完成验证,支持键盘操作与 RTL --- # SliderCaptcha 滑块验证码 `SliderCaptcha` 是一个「按住滑块拖到最右端」的验证控件,`v-model` 绑 `boolean`,`true` 表示**已验证通过**。 ## 何时使用 - 登录 / 注册 / 短信验证码等需要「一次人为停顿」的入口; - 轻量混淆脚本、降低误触强度的场景; - **不要**把它当安全边界:拖拽与键盘都能被脚本模拟,它能挡住的只是「顺手连点」,真正的风控要靠服务端。 ## 基础用法 `v-model` 绑 `boolean`;`success` 在通过的那一刻触发一次,方便埋点或联动下一步。 `text` / `successText` 可覆盖语言包文案(默认取 `captcha` 语言包分组)。 ## 尺寸 `size` 取 `small` / `medium` / `large`,高度分别跟随 `--g3-control-height-sm` / `--g3-control-height` / `--g3-control-height-lg`;手柄是正方形,宽度自动等于高度。 ## 与表单一起使用 放进 `G3FormItem` 时,label 与校验交给表单,滑块只负责提供值。验证是有状态的,**每轮提交都要复位**:清掉绑定值后调 `resume()`,让用户重新拖一次。 ## 键盘与无障碍 手柄是 `role="slider"`,带 `aria-valuemin` / `aria-valuemax` / `aria-valuenow`(0~100 的完成度)与 `aria-valuetext`(当前提示文案,通过后是成功文案),读屏可直接播报状态。 | 按键 | 行为 | | --- | --- | | `Tab` | 聚焦手柄(未通过时才可聚焦,通过后 `tabindex="-1"`) | | `→` / `↑` | 沿屏幕上「向右」推进一档(行程的 1/10,最少 20px) | | `←` / `↓` | 回退一档 | | `Home` | 回到起点(不复位通过状态) | | `End` | 直达终点并通过验证 | `keyboard={false}` 可关掉键盘通道(只保留指针拖拽)。 ## RTL 位置用逻辑属性 `inset-inline-start` 表达,镜像由 `dir` 自动完成:RTL 下手柄从**右侧**起步、向**左**拖到终点。指针位移按行内方向折算(`useDirection`),方向键的左右也随方向翻转 —— 对用户而言始终是「朝屏幕上那个方向推到底」。 ## API ### Props | 名称 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | `modelValue` | `boolean` | `false` | 双向绑定:是否已验证通过 | | `text` | `string` | 语言包 | 未通过时的提示文案 | | `successText` | `string` | 语言包 | 通过后的文案 | | `disabled` | `boolean` | `false` | 禁用态:不响应拖拽与键盘(样式不作整体半透明) | | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` | 尺寸 | | `keyboard` | `boolean` | `true` | 是否支持键盘操作 | ### Events | 名称 | 参数 | 说明 | | --- | --- | --- | | `update:modelValue` | `(value: boolean)` | 验证状态变化 | | `success` | `({ isPassing: true })` | 通过时触发一次 | | `start` | `(event: PointerEvent)` | 开始拖拽 | | `move` | `({ event, moveDistance, offset })` | 拖拽过程中触发(键盘操作不触发) | | `end` | `(event: PointerEvent)` | 拖拽结束(含取消) | ### Slots | 名称 | 参数 | 说明 | | --- | --- | --- | | `text` | `{ isPassing }` | 覆盖提示文案 | | `action` | `{ isPassing }` | 覆盖手柄里的图标(默认 `ChevronsRight` / 通过后 `Check`,RTL 下由全库的图标镜像规则翻面) | ### 实例方法 | 名称 | 签名 | 说明 | | --- | --- | --- | | `resume` | `() => void` | 复位到未通过状态(清绑定值 + 回弹动画),校验失败后调用 | ### 类型定义 ```ts export type SliderCaptchaSize = 'small' | 'medium' | 'large' export interface SliderCaptchaProps { modelValue?: boolean text?: string successText?: string disabled?: boolean size?: SliderCaptchaSize keyboard?: boolean } ``` ### 样式变量 | 变量 | 默认值 | 说明 | | --- | --- | --- | | `--g3-control-height` | `32px` | 默认高度(手柄宽 = 高度) | | `--g3-control-height-sm` | `24px` | `size="small"` 的高度 | | `--g3-control-height-lg` | `40px` | `size="large"` 的高度 | | `--g3-fill` | `rgba(0, 0, 0, 0.04)` | 轨道底色 | | `--g3-border-color` | `#d9d9d9` | 轨道描边 | | `--g3-color-success` | `hsl(144 57% 58%)` | 进度条与通过后的手柄图标色 | | `--g3-bg-container` | `#fff` | 手柄底色 | ## 实现说明 - **拖拽走 Pointer Events + `setPointerCapture`**(与 `bindDrag` / `bindResize` 同一套):指针移出组件、触屏、手写笔都能正确收尾,不需要往 `document` 上挂全局监听;捕获失败的老环境会退化成「移出即松手」,不会卡住。 - **跟手不加过渡**:拖拽期间进度条与手柄必须实时跟随指针,只有回弹 / `Home` / `End` 跳转才启用 300ms 过渡(`is-returning`),否则会有「拖不动」的手感。 - **行程 = 容器宽 - 手柄宽**:每次交互开始时量一次 `getBoundingClientRect()`,因此容器宽度自适应、字体缩放、`size` 变化都不需要监听 resize。 - **只有从「已通过」回退时才 `emit('update:modelValue', false)`**:中途松手的回弹不该对外重复报一次 `false`。 - **文案不硬编码**:默认值来自语言包 `captcha.slide` / `captcha.verified` / `captcha.sliderLabel`。 - 初始 `modelValue = true`(回填已通过状态)时,手柄在挂载后一帧落到行程末端,不会停在起点。