純 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><divclass="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) returnthis.isDragging = truethis.startX = e.clientX - this.buttonLeftdocument.addEventListener('mousemove', this.onDrag)document.addEventListener('mouseup', this.endDrag)},onDrag(e) {if (!this.isDragging) returnlet moveX = e.clientX - this.startXconst maxX = this.width - this.heightmoveX = Math.max(0, Math.min(moveX, maxX))this.buttonLeft = moveXthis.trackWidth = moveX + this.height},endDrag() {this.isDragging = falsedocument.removeEventListener('mousemove', this.onDrag)document.removeEventListener('mouseup', this.endDrag)const maxX = this.width - this.heightif (this.buttonLeft >= maxX - 5) {this.isPassed = truethis.$emit('pass')} else {this.buttonLeft = 0this.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 falseconst 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].timespeeds.push(dx / dt)}const avgSpeed = speeds.reduce((a, b) => a + b, 0) / speeds.lengthreturn avgSpeed < 2 && speeds.some(s => s < 0.5) // 有停頓、有波動 }
2. 拖動完成后調用后端接口驗證
目的:讓后端參與驗證,提升安全性。
實現方式:
在
endDrag
中加入 API 請求:async endDrag() {const maxX = this.width - this.heightif (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 = truethis.$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 = 0this.trackWidth = 0this.dragTrace = []this.isPassed = false} }
自動重置(比如 3 秒后):
if (!this.isPassed) {setTimeout(() => this.resetSlider(), 3000) }
4. 支持移動端觸摸事件(touchstart
, touchmove
, touchend
)
目的:讓組件在手機上也能正常使用。
實現方式:
添加事件監聽:
<divclass="slider-button"@mousedown="startDrag"@touchstart="startTouch"@touchmove="onTouchMove"@touchend="endTouch" >
方法定義:
methods: {startTouch(e) {this.isDragging = truethis.startX = e.touches[0].clientX - this.buttonLeft},onTouchMove(e) {if (!this.isDragging) returnconst moveX = e.touches[0].clientX - this.startXconst maxX = this.width - this.heightthis.buttonLeft = Math.max(0, Math.min(moveX, maxX))this.trackWidth = this.buttonLeft + this.heightthis.dragTrace.push({ x: e.touches[0].clientX, time: Date.now() })},endTouch() {this.isDragging = falsethis.endDrag() // 復用原有邏輯} }