欢迎光临葬花网
详情描述
HTML+JavaScript滑动验证码实现

我将创建一个美观且功能完整的滑动验证码组件。这个实现包括滑块拖动、轨迹验证、成功反馈等完整功能。

思路分析

创建滑块轨道和可拖动的滑块 实现滑块拖动逻辑 添加验证逻辑(包括轨迹分析) 设计成功/失败状态反馈 添加重新验证功能

下面是完整实现代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>滑动验证码</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            padding: 20px;
        }

        .container {
            width: 100%;
            max-width: 500px;
            background-color: white;
            border-radius: 15px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            overflow: hidden;
            padding: 30px;
        }

        .header {
            text-align: center;
            margin-bottom: 30px;
        }

        .header h1 {
            color: #333;
            font-size: 28px;
            margin-bottom: 10px;
        }

        .header p {
            color: #666;
            font-size: 16px;
        }

        .verification-area {
            background-color: #f8f9fa;
            border-radius: 10px;
            padding: 25px;
            margin-bottom: 25px;
            border: 1px solid #eaeaea;
        }

        .verification-instruction {
            display: flex;
            align-items: center;
            justify-content: space-between;
            margin-bottom: 20px;
            padding-bottom: 15px;
            border-bottom: 1px dashed #ddd;
        }

        .instruction-text {
            font-size: 16px;
            color: #333;
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .instruction-text i {
            color: #4a6cf7;
        }

        .refresh-btn {
            background-color: #f1f3f5;
            border: none;
            padding: 8px 15px;
            border-radius: 6px;
            cursor: pointer;
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 14px;
            color: #555;
            transition: all 0.3s;
        }

        .refresh-btn:hover {
            background-color: #e9ecef;
        }

        .slider-container {
            position: relative;
            height: 56px;
            background-color: #e9ecef;
            border-radius: 28px;
            display: flex;
            align-items: center;
            padding: 0 3px;
            margin-bottom: 15px;
            overflow: hidden;
        }

        .slider-track {
            position: absolute;
            left: 0;
            top: 0;
            height: 100%;
            width: 100%;
            background-color: #e9ecef;
            border-radius: 28px;
            z-index: 1;
        }

        .slider-bg {
            position: absolute;
            left: 0;
            top: 0;
            height: 100%;
            width: 100%;
            background: linear-gradient(90deg, #4a6cf7 0%, #6a8cff 100%);
            border-radius: 28px;
            opacity: 0.1;
            z-index: 1;
        }

        .slider {
            position: absolute;
            left: 0;
            top: 0;
            height: 50px;
            width: 50px;
            background: white;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: grab;
            box-shadow: 0 4px 12px rgba(74, 108, 247, 0.25);
            z-index: 3;
            transition: all 0.2s;
            user-select: none;
        }

        .slider:active {
            cursor: grabbing;
            box-shadow: 0 6px 16px rgba(74, 108, 247, 0.35);
        }

        .slider i {
            color: #4a6cf7;
            font-size: 18px;
        }

        .slider-mask {
            position: absolute;
            left: 0;
            top: 0;
            height: 100%;
            width: 50px;
            background: linear-gradient(90deg, #4a6cf7 0%, #6a8cff 100%);
            border-radius: 28px;
            z-index: 2;
            transition: width 0.1s;
        }

        .slider-text {
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #666;
            font-weight: 500;
            z-index: 2;
            user-select: none;
        }

        .status-area {
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 60px;
            margin-top: 20px;
            padding: 15px;
            border-radius: 8px;
            background-color: #f8f9fa;
            transition: all 0.3s;
        }

        .status-success {
            background-color: rgba(40, 167, 69, 0.1);
            color: #28a745;
        }

        .status-error {
            background-color: rgba(220, 53, 69, 0.1);
            color: #dc3545;
        }

        .status-text {
            display: flex;
            align-items: center;
            gap: 10px;
            font-size: 16px;
            font-weight: 500;
        }

        .status-text i {
            font-size: 20px;
        }

        .action-buttons {
            display: flex;
            gap: 15px;
            margin-top: 20px;
        }

        .submit-btn, .reset-btn {
            flex: 1;
            padding: 15px;
            border: none;
            border-radius: 8px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s;
        }

        .submit-btn {
            background-color: #4a6cf7;
            color: white;
        }

        .submit-btn:hover {
            background-color: #3a5ce5;
        }

        .submit-btn:disabled {
            background-color: #a0a0a0;
            cursor: not-allowed;
        }

        .reset-btn {
            background-color: #f8f9fa;
            color: #333;
            border: 1px solid #ddd;
        }

        .reset-btn:hover {
            background-color: #e9ecef;
        }

        .hint {
            text-align: center;
            margin-top: 25px;
            font-size: 14px;
            color: #777;
            line-height: 1.5;
        }

        @media (max-width: 600px) {
            .container {
                padding: 20px;
            }

            .action-buttons {
                flex-direction: column;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1><i class="fas fa-shield-alt"></i> 滑动验证</h1>
            <p>请按住滑块,拖动到最右侧完成验证</p>
        </div>

        <div class="verification-area">
            <div class="verification-instruction">
                <div class="instruction-text">
                    <i class="fas fa-arrows-alt-h"></i>
                    <span>向右滑动完成验证</span>
                </div>
                <button class="refresh-btn" id="refreshBtn">
                    <i class="fas fa-redo"></i>
                    <span>换一个</span>
                </button>
            </div>

            <div class="slider-container" id="sliderContainer">
                <div class="slider-track"></div>
                <div class="slider-bg"></div>
                <div class="slider-mask" id="sliderMask"></div>
                <div class="slider-text" id="sliderText">拖动滑块验证</div>
                <div class="slider" id="slider">
                    <i class="fas fa-arrows-alt-h"></i>
                </div>
            </div>

            <div class="status-area" id="statusArea">
                <div class="status-text">
                    <i class="fas fa-info-circle"></i>
                    <span>等待验证...</span>
                </div>
            </div>
        </div>

        <div class="action-buttons">
            <button class="submit-btn" id="submitBtn" disabled>提交验证</button>
            <button class="reset-btn" id="resetBtn">重置验证</button>
        </div>

        <div class="hint">
            <p><i class="fas fa-lightbulb"></i> 提示:请匀速拖动滑块,不要过快或过慢,以免验证失败</p>
        </div>
    </div>

    <script>
        // DOM元素
        const slider = document.getElementById('slider');
        const sliderContainer = document.getElementById('sliderContainer');
        const sliderMask = document.getElementById('sliderMask');
        const sliderText = document.getElementById('sliderText');
        const statusArea = document.getElementById('statusArea');
        const statusText = statusArea.querySelector('.status-text span');
        const statusIcon = statusArea.querySelector('.status-text i');
        const submitBtn = document.getElementById('submitBtn');
        const resetBtn = document.getElementById('resetBtn');
        const refreshBtn = document.getElementById('refreshBtn');

        // 验证状态
        let isVerified = false;
        let isDragging = false;
        let startX = 0;
        let startTime = 0;
        let endTime = 0;
        let dragDistance = 0;
        let dragPath = [];
        let verificationPassed = false;

        // 初始化
        function init() {
            resetVerification();
            setupEventListeners();
        }

        // 设置事件监听器
        function setupEventListeners() {
            // 滑块鼠标/触摸事件
            slider.addEventListener('mousedown', startDrag);
            slider.addEventListener('touchstart', startDragTouch);

            // 重置按钮
            resetBtn.addEventListener('click', resetVerification);

            // 刷新按钮
            refreshBtn.addEventListener('click', refreshVerification);

            // 提交按钮
            submitBtn.addEventListener('click', submitVerification);

            // 全局鼠标/触摸事件
            document.addEventListener('mousemove', doDrag);
            document.addEventListener('touchmove', doDragTouch);
            document.addEventListener('mouseup', stopDrag);
            document.addEventListener('touchend', stopDrag);
        }

        // 开始拖动(鼠标)
        function startDrag(e) {
            isDragging = true;
            startX = e.clientX - slider.offsetLeft;
            startTime = Date.now();
            dragPath = [];

            slider.style.cursor = 'grabbing';
            updateStatus('正在验证中...', 'fas fa-sync-alt fa-spin', 'default');
        }

        // 开始拖动(触摸)
        function startDragTouch(e) {
            if (e.touches.length === 1) {
                isDragging = true;
                startX = e.touches[0].clientX - slider.offsetLeft;
                startTime = Date.now();
                dragPath = [];

                slider.style.cursor = 'grabbing';
                updateStatus('正在验证中...', 'fas fa-sync-alt fa-spin', 'default');
            }
        }

        // 执行拖动(鼠标)
        function doDrag(e) {
            if (!isDragging) return;

            e.preventDefault();
            let x = e.clientX - startX;
            handleDrag(x);
        }

        // 执行拖动(触摸)
        function doDragTouch(e) {
            if (!isDragging) return;

            if (e.touches.length === 1) {
                e.preventDefault();
                let x = e.touches[0].clientX - startX;
                handleDrag(x);
            }
        }

        // 处理拖动逻辑
        function handleDrag(x) {
            const containerWidth = sliderContainer.offsetWidth;
            const sliderWidth = slider.offsetWidth;
            const maxX = containerWidth - sliderWidth;

            // 限制滑块在容器内
            if (x < 0) x = 0;
            if (x > maxX) x = maxX;

            // 更新滑块位置
            slider.style.left = x + 'px';
            sliderMask.style.width = (x + sliderWidth) + 'px';

            // 记录拖动路径
            dragPath.push({
                x: x,
                time: Date.now()
            });

            // 更新拖动距离
            dragDistance = x;

            // 检查是否到达最右侧
            if (x >= maxX - 5) {
                completeDrag();
            }
        }

        // 完成拖动
        function completeDrag() {
            if (!isDragging) return;

            isDragging = false;
            endTime = Date.now();

            // 验证拖动行为
            verificationPassed = verifyDrag();

            if (verificationPassed) {
                // 验证成功
                isVerified = true;
                submitBtn.disabled = false;
                updateStatus('验证成功!', 'fas fa-check-circle', 'success');
                sliderText.textContent = '验证成功';
                sliderText.style.color = '#28a745';
                slider.style.background = 'linear-gradient(90deg, #28a745 0%, #5cd85c 100%)';
                slider.innerHTML = '<i class="fas fa-check"></i>';
                slider.style.cursor = 'default';
            } else {
                // 验证失败
                updateStatus('验证失败,请重试', 'fas fa-times-circle', 'error');
                sliderText.textContent = '验证失败';
                sliderText.style.color = '#dc3545';

                // 1秒后重置滑块
                setTimeout(() => {
                    resetSlider();
                    updateStatus('验证失败,请重试', 'fas fa-times-circle', 'error');
                }, 1000);
            }
        }

        // 停止拖动
        function stopDrag() {
            if (!isDragging) return;

            isDragging = false;
            slider.style.cursor = 'grab';

            // 如果没有到达最右侧,则滑块回弹
            if (dragDistance < sliderContainer.offsetWidth - slider.offsetWidth - 5) {
                resetSlider();
                updateStatus('请拖动滑块到最右侧', 'fas fa-info-circle', 'default');
            }
        }

        // 重置滑块位置
        function resetSlider() {
            slider.style.left = '0px';
            sliderMask.style.width = '50px';
            sliderText.textContent = '拖动滑块验证';
            sliderText.style.color = '#666';
            slider.style.background = 'white';
            slider.innerHTML = '<i class="fas fa-arrows-alt-h"></i>';
            slider.style.cursor = 'grab';
            dragDistance = 0;
        }

        // 验证拖动行为
        function verifyDrag() {
            const dragTime = endTime - startTime;
            const containerWidth = sliderContainer.offsetWidth;
            const sliderWidth = slider.offsetWidth;
            const maxX = containerWidth - sliderWidth;

            // 检查是否到达最右侧
            if (dragDistance < maxX - 5) {
                return false;
            }

            // 检查拖动时间(不能太快也不能太慢)
            if (dragTime < 500 || dragTime > 5000) {
                return false;
            }

            // 检查拖动路径的平滑度(模拟真实拖动行为)
            if (dragPath.length < 10) {
                return false;
            }

            // 计算速度变化
            let speedChanges = [];
            for (let i = 1; i < dragPath.length; i++) {
                const distance = dragPath[i].x - dragPath[i-1].x;
                const time = dragPath[i].time - dragPath[i-1].time;
                const speed = time > 0 ? distance / time : 0;
                speedChanges.push(speed);
            }

            // 检查速度变化是否平滑(方差不能太大)
            const avgSpeed = speedChanges.reduce((a, b) => a + b, 0) / speedChanges.length;
            const variance = speedChanges.reduce((a, b) => a + Math.pow(b - avgSpeed, 2), 0) / speedChanges.length;

            // 方差太大说明拖动不平滑,可能是机器人行为
            if (variance > 0.05) {
                return false;
            }

            return true;
        }

        // 更新状态显示
        function updateStatus(text, icon, status) {
            statusText.textContent = text;
            statusIcon.className = icon;

            // 移除所有状态类
            statusArea.classList.remove('status-success', 'status-error');

            // 添加新状态类
            if (status === 'success') {
                statusArea.classList.add('status-success');
            } else if (status === 'error') {
                statusArea.classList.add('status-error');
            }
        }

        // 重置验证
        function resetVerification() {
            isVerified = false;
            verificationPassed = false;
            submitBtn.disabled = true;
            resetSlider();
            updateStatus('等待验证...', 'fas fa-info-circle', 'default');
        }

        // 刷新验证
        function refreshVerification() {
            resetVerification();
            updateStatus('验证已刷新,请重新拖动', 'fas fa-sync-alt', 'default');

            // 添加一个简单的动画效果
            refreshBtn.querySelector('i').classList.add('fa-spin');
            setTimeout(() => {
                refreshBtn.querySelector('i').classList.remove('fa-spin');
            }, 500);
        }

        // 提交验证
        function submitVerification() {
            if (!isVerified) {
                updateStatus('请先完成滑块验证', 'fas fa-exclamation-circle', 'error');
                return;
            }

            updateStatus('验证提交成功!', 'fas fa-check-circle', 'success');

            // 在实际应用中,这里会向服务器发送验证结果
            // 模拟API调用延迟
            setTimeout(() => {
                alert('验证成功!表单可以正常提交了。\n\n在实际应用中,这里会提交表单或继续下一步操作。');
            }, 300);
        }

        // 初始化验证码
        window.onload = init;
    </script>
</body>
</html>

功能说明

滑块拖动验证

  • 用户可以拖动滑块到最右侧
  • 滑块位置和背景会实时更新
  • 提供视觉反馈

验证逻辑

  • 检查是否拖动到最右侧
  • 验证拖动时间(不能太快或太慢)
  • 分析拖动路径的平滑度
  • 防止机器人行为(通过速度变化分析)

用户反馈

  • 实时显示验证状态
  • 成功/失败的不同视觉样式
  • 操作提示信息

额外功能

  • 重置验证按钮
  • 刷新验证按钮
  • 提交验证结果按钮

这个实现模拟了真实滑动验证码的核心功能,包括防止机器人行为的简单检测。在实际应用中,后端还需要进行更复杂的验证,例如分析拖动轨迹、IP检测等。

相关帖子
株洲市商城网站建设设计#电商网站建设,优秀设计团队
株洲市商城网站建设设计#电商网站建设,优秀设计团队
株洲市一站式殡葬服务|丧葬一站式服务,白事灵堂策划
株洲市一站式殡葬服务|丧葬一站式服务,白事灵堂策划
业主委员会成员需要具备哪些基本能力,日常主要处理哪些类型的事务?
业主委员会成员需要具备哪些基本能力,日常主要处理哪些类型的事务?
株洲市殡葬公司电话|白事服务一条龙,丧礼吊唁
株洲市殡葬公司电话|白事服务一条龙,丧礼吊唁
租客自行更换门锁或进行小改装,会影响押金的退还吗?
租客自行更换门锁或进行小改装,会影响押金的退还吗?
当我们谈论“以旧换新”时,是否无意中助长了过度消费和资源浪费的循环?
当我们谈论“以旧换新”时,是否无意中助长了过度消费和资源浪费的循环?
乐山市殡葬服务一条龙办理-殡葬追思会服务,有竞争力的价格
乐山市殡葬服务一条龙办理-殡葬追思会服务,有竞争力的价格
有哪些容易被忽略的PPT操作技巧,能极大提升你的制作速度?
有哪些容易被忽略的PPT操作技巧,能极大提升你的制作速度?
2026年新型隔代育儿补贴形式探索,除现金外还有哪些支持服务?
2026年新型隔代育儿补贴形式探索,除现金外还有哪些支持服务?
如何在家庭与社区中普及祭祀用火的安全知识,有效预防火灾发生?
如何在家庭与社区中普及祭祀用火的安全知识,有效预防火灾发生?
除了查看日期,还有哪些可靠的感官指标能帮助我们判断食物安全性?
除了查看日期,还有哪些可靠的感官指标能帮助我们判断食物安全性?
济宁市殡葬一站式服务|办理白事服务,殡仪殡葬灵堂
济宁市殡葬一站式服务|办理白事服务,殡仪殡葬灵堂
烟台市精准获客@独立网站建设,价格透明
烟台市精准获客@独立网站建设,价格透明
安庆市专业网站建设#安卓app开发,服务可靠
安庆市专业网站建设#安卓app开发,服务可靠
购买不同品牌的新能源汽车,其合作的充电网络费用是否存在明显差别?
购买不同品牌的新能源汽车,其合作的充电网络费用是否存在明显差别?
自贡市办理白事服务-火化入盒,价格合理
自贡市办理白事服务-火化入盒,价格合理
零工工作者在提供服务过程中受伤或发生意外,责任认定与保障机制是怎样的?
零工工作者在提供服务过程中受伤或发生意外,责任认定与保障机制是怎样的?
黄冈市短视频运营推广@企业网站建设公司,收费透明
黄冈市短视频运营推广@企业网站建设公司,收费透明
淄博市殡葬一条龙公司|白事一站式服务,葬礼吊唁
淄博市殡葬一条龙公司|白事一站式服务,葬礼吊唁
黔南品牌网站开发设计#手机app开发,一站式建站服务
黔南品牌网站开发设计#手机app开发,一站式建站服务