纯 Vue 2 实现的滑块拖动验证组件
效果说明
拖动滑块到最右侧判定为验证成功
支持自定义宽度、高度、颜色、提示文字
可扩展轨迹分析或后端验证逻辑
Vue 2 滑块验证组件代码
SliderVerify.vue
注意:icon图标使用的是Element ui图标
<template>
<div class="slider-container" :style="{ width: width + 'px', height: height + 'px' }">
<div class="slider-track" :style="{ backgroundColor: trackColor, width: trackWidth + 'px' }"></div>
<div
class="slider-button"
:style="{ left: buttonLeft + 'px', backgroundColor: buttonColor }"
@mousedown="startDrag"
>
<span v-if="!isPassed"> <i class="el-icon-d-arrow-right"></i></span>
<span v-else><i class="el-icon-check"></i></span>
</div>
<div class="slider-text">{{ isPassed ? successText : text }}</div>
</div>
</template>
<script>
export default {
name: 'SliderVerify',
props: {
width: { type: Number, default: 300 },
height: { type: Number, default: 40 },
text: { type: String, default: '请将滑块拖动到右侧' },
successText: { type: String, default: '验证成功' },
trackColor: { type: String, default: '#4caf50' },
buttonColor: { type: String, default: '#fff' }
},
data() {
return {
isDragging: false,
startX: 0,
buttonLeft: 0,
trackWidth: 0,
isPassed: false
}
},
methods: {
startDrag(e) {
if (this.isPassed) return
this.isDragging = true
this.startX = e.clientX - this.buttonLeft
document.addEventListener('mousemove', this.onDrag)
document.addEventListener('mouseup', this.endDrag)
},
onDrag(e) {
if (!this.isDragging) return
let moveX = e.clientX - this.startX
const maxX = this.width - this.height
moveX = Math.max(0, Math.min(moveX, maxX))
this.buttonLeft = moveX
this.trackWidth = moveX + this.height
},
endDrag() {
this.isDragging = false
document.removeEventListener('mousemove', this.onDrag)
document.removeEventListener('mouseup', this.endDrag)
const maxX = this.width - this.height
if (this.buttonLeft >= maxX - 5) {
this.isPassed = true
this.$emit('pass')
} else {
this.buttonLeft = 0
this.trackWidth = 0
}
}
}
}
</script>
<style scoped>
.slider-container {
position: relative;
background: #d3d3d3;
border-radius: 4px;
user-select: none;
}
.slider-track {
position: absolute;
top: 0;
left: 0;
height: 100%;
transition: width 0.2s;
border-radius: 4px;
}
.slider-button {
position: absolute;
top: 0;
width: 40px;
height: 100%;
text-align: center;
line-height: 40px;
font-size: 18px;
cursor: pointer;
border-radius: 4px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
z-index: 2;
}
.slider-text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
line-height: 40px;
text-align: center;
font-size: 14px;
color: #666;
z-index: 1;
}
</style>
使用方式
在父组件中:
<SliderVerify @pass="handleVerifySuccess" />
<script>
import SliderVerify from "@/components/Login/SliderVerify.vue";
export default {
components: {
SliderVerify
},
data() {
return {
};
},
methods: {
handleVerifySuccess() {
console.log('验证通过!')
// 可执行登录、提交等操作
}
},
};
</script>
可扩展方向(以下内容仅提供思路)
✅ 加入拖动轨迹分析(防机器人)
✅ 拖动完成后调用后端接口验证
✅ 添加“重置”按钮或自动重置逻辑
✅ 支持移动端触摸事件(
touchstart
,touchmove
,touchend
)
1. 加入拖动轨迹分析(防机器人)
目的:判断用户是否是人类,通过拖动轨迹的“自然性”来识别。
实现思路:
在
onDrag
方法中记录每一次拖动的时间戳和位置:data() { return { dragTrace: [] // [{x: 123, time: 123456789}] } }, methods: { onDrag(e) { const now = Date.now() this.dragTrace.push({ x: e.clientX, time: now }) // 原有拖动逻辑... } }
拖动完成后分析轨迹:
endDrag() { // 轨迹分析:速度是否过快、是否有停顿、是否线性过于完美 const isHumanLike = this.analyzeTrace(this.dragTrace) if (!isHumanLike) { alert('行为异常,请重试') this.resetSlider() return } // 原有验证逻辑... }, analyzeTrace(trace) { if (trace.length < 5) return false const speeds = [] for (let i = 1; i < trace.length; i++) { const dx = Math.abs(trace[i].x - trace[i - 1].x) const dt = trace[i].time - trace[i - 1].time speeds.push(dx / dt) } const avgSpeed = speeds.reduce((a, b) => a + b, 0) / speeds.length return avgSpeed < 2 && speeds.some(s => s < 0.5) // 有停顿、有波动 }
2. 拖动完成后调用后端接口验证
目的:让后端参与验证,提升安全性。
实现方式:
在
endDrag
中加入 API 请求:async endDrag() { const maxX = this.width - this.height if (this.buttonLeft >= maxX - 5) { try { const res = await fetch('/api/verify-slider', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ trace: this.dragTrace }) }) const result = await res.json() if (result.success) { this.isPassed = true this.$emit('pass') } else { alert('验证失败,请重试') this.resetSlider() } } catch (err) { console.error('验证接口异常', err) this.resetSlider() } } else { this.resetSlider() } }
3. 添加“重置”按钮或自动重置逻辑
目的:用户拖错了可以重新尝试。
实现方式:
添加按钮:
<button v-if="!isPassed" @click="resetSlider" class="reset-btn">重置</button>
方法定义:
methods: { resetSlider() { this.buttonLeft = 0 this.trackWidth = 0 this.dragTrace = [] this.isPassed = false } }
自动重置(比如 3 秒后):
if (!this.isPassed) { setTimeout(() => this.resetSlider(), 3000) }
4. 支持移动端触摸事件(touchstart
, touchmove
, touchend
)
目的:让组件在手机上也能正常使用。
实现方式:
添加事件监听:
<div class="slider-button" @mousedown="startDrag" @touchstart="startTouch" @touchmove="onTouchMove" @touchend="endTouch" >
方法定义:
methods: { startTouch(e) { this.isDragging = true this.startX = e.touches[0].clientX - this.buttonLeft }, onTouchMove(e) { if (!this.isDragging) return const moveX = e.touches[0].clientX - this.startX const maxX = this.width - this.height this.buttonLeft = Math.max(0, Math.min(moveX, maxX)) this.trackWidth = this.buttonLeft + this.height this.dragTrace.push({ x: e.touches[0].clientX, time: Date.now() }) }, endTouch() { this.isDragging = false this.endDrag() // 复用原有逻辑 } }