更新记录

1.0.0(2026-09-03) 下载此版本

行为验证组件(三合一) SL-WX-Captcha 滑块/补全/文字验证


平台兼容性

其他

多语言 暗黑模式 宽屏模式

SL-WX-Captcha 行为验证组件(三合一)

组件简介

单一组件统一支持三种主流前端行为验证方式,通过 mode 属性零切换:

  • slide 滑动验证:经典滑块,按住并拖动到底部最右侧即视为通过,失败自动回弹。

  • puzzle 拼图验证:背景图上随机生成一个缺口位置,拖动下方滑块让图片拼图块精准对齐缺口,容差可配置。

  • click 顺序点字验证:背景图上随机散布多个汉字(目标字 + 干扰字),用户按提示依次点击所有目标字(2\~5 个可配)。

组件内部已兼容 H5(鼠标 mousedown/move/up)微信小程序(touchstart/move/end) 两套事件链路,失败/成功回调、刷新、关闭、容差 / 目标字数等全部参数化可控制。

注:当前背景图使用 picsum.photos 随机种子作为纯前端 mock 演示;生产环境中建议由后端生成带签名 / 随机水印的验证图与答案,并在 success 回调里携带后端校验。

目录结构

components/SL-WX-Captcha
└── SL-WX-Captcha.vue

三种模式对比

模式值 名称 成功判定 失败后处理
slide 滑动验证 滑块进度 ≥ 98%(即已滑到最右,自动吸附 100%) 400ms easeOutQuad 回弹到 0
puzzle 拼图验证 |拼图块中心 − 缺口中心| ≤ puzzleTolerance(默认 4px),成功自动吸附到精准位置 450ms easeOutQuad 回弹至 0
click 顺序点字 依次点中所有目标字,任意一步点错立即失败(也支持「确认」按钮手动提交) 700\~900ms 自动清空重试

实现要点 & 修复日志

滑动 / 拼图滑块显示与拖拽可用修复

问题 修复方案
滑块初始不可见(slideX=0 时 thumb 完全在轨道外) 原写法 left: X%; transform: translate(-100%, 0) 叠加时,X=0 → translate(-100%) 会把 thumb 向左平移一个 thumb 自身宽度挪到轨道外。改为纯像素 left: Npx + top:50%; translateY(-50%) 垂直居中slideThumbPx / puzzleThumbPx 直接从 0 线性增长到 轨道宽 - thumb 宽,保证两端视觉与比例完全对齐。
拼图块无内容、跟缺口对不上 原写法里 .sl-puzzle-piece__imgwidth:100% mode=aspectFill,展示的是拼图块左上角对应底图的 (0,0) 位置——根本不对应缺口的像素。改为:piece 里的 <image> 宽高强制等于舞台宽高,再 transform: translate(-pieceLeft, -pieceTop) 反向偏移,这样 piece 滑到哪就显示底图对应哪一像素,对齐缺口时内容完全吻合。
缺口形状不明显、不显示 原缺口只是一个半透明方块,不够像拼图。改为 clip-path: polygon() 切出「正方形主体 + 右侧半圆凸块」的经典拼图形状,gap(缺口蒙版)和 piece(拼图块)共用同一份 clip-path,形状完全吻合。
首次渲染拖不动 _sliderWidth 依赖 SelectorQuery 异步回调结果,mounted 首次 touchmove 时回调可能还没返回 → 0 导致 dx 被除。onSlideStart / onPuzzleStart 里先调用 _ensureSizesFallback()width prop 先兜底换算一套可用的轨道/thumb 尺寸,保证拖拽启动瞬间就有正确行程。
H5 拖出滑块外"卡住手势" 在组件 mounteddocument.addEventListener('mousemove' / 'mouseup')beforeDestroy 统一解绑,避免 mousedown 后拖到滑块外松开导致的"再按下去无响应"。
click 模式点字事件平台差异 小程序端用 @touchend + changedTouches[0];H5 端单独绑定 @tap.stop.prevente.clientX/Y,避免某一端取不到坐标。

失败回弹曲线

slide / puzzle 失败后不再用 setTimeout + 一次性跳回,改用 requestAnimationFrame + easeOutQuad 逐帧回弹到 0,视觉更自然。progress(fail 回调)为换算后的百分比,便于日志。

Props

属性 类型 默认值 说明
mode String 'slide' 验证模式:slide / puzzle / click
width Number|String 620 组件宽度;Number 视为 rpx,String 直接作为 CSS 值
puzzleHeight Number 320 拼图 / 点字模式图片舞台高度(rpx)
trackHeight Number 72 滑块条高度(rpx)
radius Number 16 拼图 / 点字模式圆角(rpx)
pieceSize Number 100 拼图块边长(rpx),建议为舞台宽度的 1/5\~1/6
puzzleTolerance Number 4 拼图容差(像素 px):拼图块中心与缺口中心的像素差。生产推荐 24px,宽松体验 68px
slideLabel String '请按住滑块,拖动到最右边' 模式 1 的提示文案
targetCount Number 3 模式 3 的目标文字数量,必须为 2\~5。总随机字数 = targetCount + distractCount
distractCount Number 4 模式 3 的干扰字数量
showHeader Boolean true 是否显示顶部标题与刷新栏
showClose Boolean false 是否在顶部栏显示「✕」关闭按钮
title String '' 自定义顶部标题,留空则根据模式自动取「滑动验证 / 补全图片验证 / 文字顺序验证」

Events

事件名 回调参数 说明
success { mode, type, ...payload } 验证通过触发,type 为对应模式名:slide / puzzle / click
fail { mode, type, ...payload } 验证失败触发
refresh { mode } 点击顶部刷新图标(⟳)触发
close - 点击顶部关闭按钮(✕)触发,需要 showClose = true

success / fail 的 payload 详情

// mode = 'slide'
success: { mode: 'slide', type: 'slide' }
fail:    { mode: 'slide', type: 'slide', progress: Number }   // progress 失败时的进度 0~100

// mode = 'puzzle'
success: { mode: 'puzzle', type: 'puzzle', diff: 1.3, tolerance: 4 }   // diff=像素偏差,tolerance=props设定
fail:    { mode: 'puzzle', type: 'puzzle', diff: 18.6, tolerance: 4 }

// mode = 'click'
success: { mode: 'click', type: 'click', seq: [0,1,2] }       // seq=用户依次点击的目标索引
fail:    { mode: 'click', type: 'click', reason: 'wrong-order-or-miss' | 'incomplete' }

对外方法

通过 $refs.captcha.xxx() 调用:

方法名 说明
onRefresh() 与点击顶部刷新按钮等效:重置所有状态、重新随机背景图、缺口位置、点字分布。模式 2 / 3 切换参数(如 targetCount)后建议手动调用一次。
onClose() 触发 close 事件。
noop() 空函数,用作事件占位。

使用示例

示例 1:基础滑块验证(登录/注册前)

<template>
  <view>
    <sl-wx-captcha
      ref="cap"
      mode="slide"
      :width="660"
      slide-label="请按住滑块,向右拖动完成验证"
      @success="onCapOk"
      @fail="onCapFail"
    />
  </view>
</template>

<script>
import SlWxCaptcha from '@/components/SL-WX-Captcha/SL-WX-Captcha.vue'
export default {
  components: { SlWxCaptcha },
  methods: {
    onCapOk() {
      uni.showToast({ title: '验证通过,正在登录', icon: 'none' })
      this.submitLogin() // 验证成功后再调用登录接口
    },
    onCapFail(e) {
      console.log('滑块未到底:', e.progress + '%')
    }
  }
}
</script>

示例 2:拼图验证(容差 6px,较宽松)

<template>
  <sl-wx-captcha
    ref="cap"
    mode="puzzle"
    :puzzle-tolerance="6"
    :width="620"
    :radius="20"
    @success="onSuccess"
    @fail="onFail"
    @refresh="onRefresh"
  />
</template>

示例 3:顺序点字(4 字验证,作为重要操作二次确认)

<template>
  <sl-wx-captcha
    ref="cap"
    mode="click"
    :target-count="4"
    :distract-count="5"
    :show-close="true"
    title="请先完成二次验证"
    @success="onPay"
    @close="onCancel"
  />
</template>

<script>
export default {
  methods: {
    onPay(e) {
      // e.seq 可上报后端进行二次审计
      console.log('验证通过,点字顺序:', e.seq)
      this.confirmPay()
    },
    onCancel() {
      uni.navigateBack()
    }
  }
}
</script>

示例 4:弹层(uni-popup / 自定义遮罩)内使用 + 关闭按钮 + 刷新

<template>
  <view class="mask" @tap="close"></view>
  <view class="pop" @tap.stop="">
    <sl-wx-captcha
      ref="cap"
      :mode="mode"
      :show-close="true"
      @success="onOk"
      @close="close"
      @refresh="(e)=>console.log('刷新了', e.mode)"
    />
  </view>
</template>

实现要点(可二次开发)

  1. 事件链路

    • 滑块 / 拼图使用 @touchstart/move/end/capture.stop.prevent 保证在小程序端优先锁定手势,避免父容器 scroll-view 把横拖误识别为纵向滚动。

    • H5 端在组件 mounteddocument 级绑定 mousemove / mouseupbeforeDestroy 自动解绑,防止鼠标抬出滑块外导致"卡着手势"。

  2. 测量prepareStage() 中用 uni.createSelectorQuery().in(this) 取舞台和滑块条的真实像素宽度,兼容不同宽度配置下拼图比例/滑块行程正确。
  3. 拼图实现

    • 用「相同背景图 × 2」一张铺底,另一张放入 .sl-puzzle-pieceoverflow:hidden + 固定宽高 = 拼图块),通过 piece 的 top/left 与滑块位置联动实现"切片效果",无需后端合成拼图。

    • 缺口用半透明白色蒙版 + 内描边,提示用户拼图目标位置。

  4. 点字命中判定:以用户点击像素为圆心,半径 26px 范围内取最近字作为命中候选,字的样式随机倾斜/颜色/大小/字体,贴近真实点字验证码观感。
  5. 无障碍与反馈:每次失败或成功都会在舞台顶部弹出半透明遮罩(绿色通过/红色失败),并且统一触发 success/fail 事件便于接入业务。

注意事项

  1. 拼图背景图域名白名单:如果使用小程序端,请在微信公众平台配置 picsum.photos 为合法 downloadFile 域名;生产时换成自己的 OSS/CDN 即可。
  2. 图片加载失败兜底onPuzzleImgError 已经监听了底图失败(会走白底 + 缺口形状),交互依然可用,建议后端额外监控图片 404。
  3. 拼图容差与用户体验puzzleTolerance 建议 2\~4(难)\~ 6\~8(宽松),超过 10px 基本等同于无校验。
  4. 不要把安全校验放前端:本组件实现的是前端交互基础判定,在前端判定成功后建议把 diff/seq/progress 等摘要一并上报后端,使用后端接口做最终决策(或使用加密 token),避免被自动化脚本绕过。
  5. mode 切换时自动重绘:组件已 watch mode,切换会立即 resetAll + prepareStage,无需手动刷新;但如果是在同一 mode 下改了 targetCount / puzzleTolerance 等参数,建议调用 this.$refs.cap.onRefresh() 手动刷新一次题目。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议