更新记录
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__img 用 width: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 拖出滑块外"卡住手势" | 在组件 mounted 时 document.addEventListener('mousemove' / 'mouseup'),beforeDestroy 统一解绑,避免 mousedown 后拖到滑块外松开导致的"再按下去无响应"。 |
| click 模式点字事件平台差异 | 小程序端用 @touchend + changedTouches[0];H5 端单独绑定 @tap.stop.prevent 取 e.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):拼图块中心与缺口中心的像素差。生产推荐 2 |
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>
实现要点(可二次开发)
-
事件链路:
-
滑块 / 拼图使用
@touchstart/move/end/capture.stop.prevent保证在小程序端优先锁定手势,避免父容器 scroll-view 把横拖误识别为纵向滚动。 -
H5 端在组件
mounted时document级绑定mousemove / mouseup,beforeDestroy自动解绑,防止鼠标抬出滑块外导致"卡着手势"。
-
- 测量:
prepareStage()中用uni.createSelectorQuery().in(this)取舞台和滑块条的真实像素宽度,兼容不同宽度配置下拼图比例/滑块行程正确。 -
拼图实现:
-
用「相同背景图 × 2」一张铺底,另一张放入
.sl-puzzle-piece(overflow:hidden+ 固定宽高 = 拼图块),通过 piece 的top/left与滑块位置联动实现"切片效果",无需后端合成拼图。 -
缺口用半透明白色蒙版 + 内描边,提示用户拼图目标位置。
-
- 点字命中判定:以用户点击像素为圆心,半径 26px 范围内取最近字作为命中候选,字的样式随机倾斜/颜色/大小/字体,贴近真实点字验证码观感。
- 无障碍与反馈:每次失败或成功都会在舞台顶部弹出半透明遮罩(绿色通过/红色失败),并且统一触发
success/fail事件便于接入业务。
注意事项
- 拼图背景图域名白名单:如果使用小程序端,请在微信公众平台配置
picsum.photos为合法 downloadFile 域名;生产时换成自己的 OSS/CDN 即可。 - 图片加载失败兜底:
onPuzzleImgError已经监听了底图失败(会走白底 + 缺口形状),交互依然可用,建议后端额外监控图片 404。 - 拼图容差与用户体验:
puzzleTolerance建议 2\~4(难)\~ 6\~8(宽松),超过 10px 基本等同于无校验。 - 不要把安全校验放前端:本组件实现的是前端交互和基础判定,在前端判定成功后建议把
diff/seq/progress等摘要一并上报后端,使用后端接口做最终决策(或使用加密 token),避免被自动化脚本绕过。 - mode 切换时自动重绘:组件已 watch
mode,切换会立即resetAll + prepareStage,无需手动刷新;但如果是在同一 mode 下改了targetCount / puzzleTolerance等参数,建议调用this.$refs.cap.onRefresh()手动刷新一次题目。

收藏人数:
下载插件并导入HBuilderX
下载示例项目ZIP
赞赏(0)
下载 104
赞赏 0
下载 12559264
赞赏 1948
赞赏
京公网安备:11010802035340号