170 lines
4.1 KiB
Vue
170 lines
4.1 KiB
Vue
<template>
|
||
<!-- 滑动验证码容器 -->
|
||
<div class="slider-container" ref="containerRef">
|
||
<!-- 滑动遮罩层,显示滑动进度 -->
|
||
<div class="slider-mask" :style="{ width: sliderMaskWidth }" />
|
||
<!-- 滑块按钮 -->
|
||
<div
|
||
class="slider"
|
||
ref="sliderRef"
|
||
@mousedown="handleDragStart"
|
||
:class="{ 'slider-success': verified }"
|
||
:style="{ left: sliderLeft }"
|
||
>
|
||
<!-- 滑块图标 -->
|
||
<Icon icon="ant-design:double-right-outlined" :size="20" />
|
||
</div>
|
||
<!-- 提示文本 -->
|
||
<div class="slider-text" :class="{ 'slider-text-success': verified }">
|
||
{{ verified ? "验证通过" : "将滑块拖动至右侧完成验证" }}
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, defineEmits, onUnmounted } from "vue"
|
||
|
||
const emit = defineEmits(["verifySuccess"])
|
||
|
||
// DOM 引用
|
||
const containerRef = ref<HTMLElement | null>(null) // 容器元素引用
|
||
const sliderRef = ref<HTMLElement | null>(null) // 滑块元素引用
|
||
|
||
// 状态变量
|
||
const sliderLeft = ref("0px") // 滑块左侧位置
|
||
const sliderMaskWidth = ref("0px") // 遮罩层宽度
|
||
const verified = ref(false) // 验证状态
|
||
|
||
// 拖动相关变量
|
||
let startX = 0 // 开始拖动时的 X 坐标
|
||
let sliderLeft_temp = 0 // 临时存储滑块位置
|
||
|
||
/**
|
||
* 开始拖动处理
|
||
* @param e 鼠标事件对象
|
||
*/
|
||
const handleDragStart = (e: MouseEvent) => {
|
||
if (verified.value) return // 如果已验证通过,不再处理
|
||
startX = e.clientX
|
||
sliderLeft_temp = parseInt(sliderLeft.value)
|
||
// 添加鼠标移动和松开事件监听
|
||
document.addEventListener("mousemove", handleDragMove)
|
||
document.addEventListener("mouseup", handleDragEnd)
|
||
}
|
||
|
||
/**
|
||
* 拖动过程处理
|
||
* @param e 鼠标事件对象
|
||
*/
|
||
const handleDragMove = (e: MouseEvent) => {
|
||
if (verified.value) return
|
||
const container = containerRef.value
|
||
if (!container) return
|
||
|
||
// 计算拖动距离
|
||
const diff = e.clientX - startX
|
||
const containerWidth = container.offsetWidth
|
||
const sliderWidth = sliderRef.value?.offsetWidth || 0
|
||
const maxLeft = containerWidth - sliderWidth
|
||
|
||
// 计算新位置并限制范围
|
||
let newLeft = sliderLeft_temp + diff
|
||
if (newLeft < 0) newLeft = 0
|
||
if (newLeft > maxLeft) newLeft = maxLeft
|
||
|
||
// 更新滑块和遮罩层位置
|
||
sliderLeft.value = newLeft + "px"
|
||
sliderMaskWidth.value = newLeft + sliderWidth / 2 + "px"
|
||
|
||
// 检查是否验证成功
|
||
if (newLeft === maxLeft) {
|
||
verified.value = true
|
||
emit("verifySuccess", true) // 触发验证成功
|
||
handleDragEnd()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 结束拖动处理
|
||
*/
|
||
const handleDragEnd = () => {
|
||
if (!verified.value) {
|
||
// 如果未验证成功,重置位置
|
||
sliderLeft.value = "0px"
|
||
sliderMaskWidth.value = "0px"
|
||
}
|
||
// 移除事件监听
|
||
document.removeEventListener("mousemove", handleDragMove)
|
||
document.removeEventListener("mouseup", handleDragEnd)
|
||
}
|
||
|
||
// 组件卸载时清理事件监听
|
||
onUnmounted(() => {
|
||
document.removeEventListener("mousemove", handleDragMove)
|
||
document.removeEventListener("mouseup", handleDragEnd)
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* 滑动验证码容器样式 */
|
||
.slider-container {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 42px;
|
||
background-color: #f5f5f5;
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 8px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
/* 滑动遮罩层样式 */
|
||
.slider-mask {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
height: 100%;
|
||
background-color: #d1e9fc;
|
||
border-radius: 8px;
|
||
transition: width 0.1s linear;
|
||
}
|
||
|
||
/* 滑块按钮样式 */
|
||
.slider {
|
||
position: absolute;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 60px;
|
||
height: 42px;
|
||
background: #e1dede;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: background-color 0.2s;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: white;
|
||
}
|
||
|
||
/* 验证成功时滑块样式 */
|
||
.slider-success {
|
||
background-color: #1ea0fa;
|
||
}
|
||
|
||
/* 提示文本样式 */
|
||
.slider-text {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
user-select: none;
|
||
font-weight: 400;
|
||
font-size: 12px;
|
||
color: #9cb3c5;
|
||
width: auto;
|
||
}
|
||
|
||
/* 验证成功时文本样式 */
|
||
.slider-text-success {
|
||
color: #1ea0fa;
|
||
}
|
||
</style> |