Skip to content

站点停靠验证 (CheckpointCaptcha)

基于按压蓄力的交互式验证码。用户需要将滑块拖动到指定站点,并长按进行"充电",待电量充满(变绿)后即可完成验证。这种交互方式能有效采集用户的按压时长和微小抖动,极大提高人机识别准确率。

交互流程

  1. 用户拖动滑块至中间的"蓝色站点"位置。
  2. 按住鼠标左键(或手指不离开屏幕),此时会出现充电进度提示。
  3. 当圆圈变绿(充电完成)后,物理阻挡消失。
  4. 继续向右拖动滑块至终点释放,完成验证。

基础用法

html

<div id="captcha-container"></div>

<script>
    // 创建 CheckpointCaptcha 实例
    const checkpoint = new WlzShield.CheckpointCaptcha({
        siteKey: 'your-site-key',          // 必填:网站唯一标识
        containerId: '#captcha-container', // 必填:挂载容器ID
        width: '100%'                      // 组件宽度(默认100%)
    });

    // 可选:监听验证结果
    checkpoint.config.onSuccess = function (data) {
        console.log('验证成功:', data);
        // 将验证凭证提交到您的后端
    };
</script>

在线体验

配置参数 (API)

基础配置

参数名类型默认值是否必填说明
siteKeystring-必填网站唯一标识,用于区分不同站点的验证请求
containerIdstring-必填DOM 容器 ID 选择器 (如 #captcha-box)
challengeUrlstring/api/challenge可选后端验证接口地址
widthnumber | string'100%'可选组件宽度,支持数字(px)或CSS字符串(如 '100%', '320px')
langstringzh可选语言设置:'zh' (中文) 或 'en' (英文)
mode'embed' | 'popup''embed'可选展示模式:'embed' (嵌入式) 或 'popup' (弹窗式)
timeoutnumber10000可选请求超时时间(毫秒)

高级配置

参数名类型默认值说明
customTextsRecord<string, string>{}自定义文案,覆盖默认的按钮文本、提示信息等
customStylesPartial<CSSStyleDeclaration>{}自定义主容器样式,应用于整个验证码组件
popupStylesPartial<CSSStyleDeclaration>{}仅对popup模式有效,自定义弹窗样式

回调函数

参数名类型默认值说明
onReady() => voidundefined组件加载完成时触发
onOpen() => voidundefined仅popup模式有效,弹窗打开时触发
onClose() => voidundefined仅popup模式有效,弹窗关闭时触发
onSuccess(data: any) => voidundefined验证通过时触发
onFailure(data: any) => voidundefined验证失败时触发(逻辑拒绝,如轨迹异常)
onError(error: Error) => voidundefined系统/网络错误时触发(如 404, 500, 网络断开)

详细配置说明

模式选择 (mode)

CheckpointCaptcha 支持两种展示模式:

javascript
// 嵌入式模式(默认):直接在当前容器中显示验证码
const embedCaptcha = new WlzShield.CheckpointCaptcha({
    siteKey: 'your-site-key',
    containerId: '#captcha-box',
    mode: 'embed', // 默认值
    width: '100%'
});

// 弹窗式模式:显示触发按钮,点击后弹出验证窗口
const popupCaptcha = new WlzShield.CheckpointCaptcha({
    siteKey: 'your-site-key',
    containerId: '#captcha-box',
    mode: 'popup',
    customTexts: {
        buttonText: '点击验证' // 弹窗触发按钮文本
    }
});

自定义文案 (customTexts)

您可以覆盖以下默认文案:

javascript
const checkpoint = new WlzShield.CheckpointCaptcha({
    siteKey: 'your-site-key',
    containerId: '#captcha-container',
    customTexts: {
        // 覆盖描述文本(支持HTML)
        desc: "拖动滑块到<b>蓝色充电站</b>,按住充电",
        // 覆盖充电中文本
        charging: "蓄力中",
        // 覆盖成功文本
        success: "✅ 验证成功",
        // 覆盖失败文本
        fail: "验证失败",
        // 覆盖重试文本
        retry: "点击重试",
        // 弹窗模式下的按钮文本(仅popup模式有效)
        buttonText: "开始验证",
        // 弹窗标题(仅popup模式有效)
        popupTitle: "安全验证"
    }
});

自定义样式 (customStyles)

您可以调整验证码组件的外观样式:

javascript
const checkpoint = new WlzShield.CheckpointCaptcha({
    siteKey: 'your-site-key',
    containerId: '#captcha-container',
    mode: 'embed',
    customStyles: {
        // 修改背景色
        backgroundColor: '#f0f9ff',
        // 添加边框
        border: '1px solid #bae6fd',
        // 调整圆角
        borderRadius: '12px',
        // 调整内边距
        padding: '20px',
        // 修改字体
        fontFamily: '"Segoe UI", system-ui, sans-serif',
        // 添加阴影
        boxShadow: '0 4px 12px rgba(0,0,0,0.08)'
    }
});

弹窗样式自定义 (popupStyles)

仅当 mode: 'popup' 时有效:

javascript
const checkpoint = new WlzShield.CheckpointCaptcha({
    siteKey: 'your-site-key',
    containerId: '#captcha-container',
    mode: 'popup',
    popupStyles: {
        // 弹窗宽度
        maxWidth: '420px',
        // 弹窗背景色
        backgroundColor: '#ffffff',
        // 弹窗边框
        border: 'none',
        // 弹窗圆角
        borderRadius: '16px',
        // 弹窗阴影
        boxShadow: '0 10px 40px rgba(0,0,0,0.15)'
    }
});

完整示例

示例1:表单验证集成(嵌入式模式)

html
<!DOCTYPE html>
<html>
<head>
    <title>站点停靠验证示例</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            padding: 40px;
            max-width: 500px;
            margin: 0 auto;
        }

        .form-container {
            background: #f8fafc;
            padding: 30px;
            border-radius: 12px;
            border: 1px solid #e2e8f0;
        }

        .form-group {
            margin-bottom: 20px;
        }

        label {
            display: block;
            margin-bottom: 8px;
            font-weight: 600;
            color: #334155;
        }

        input {
            width: 100%;
            padding: 12px;
            border: 1px solid #cbd5e1;
            border-radius: 6px;
            font-size: 14px;
        }

        button {
            width: 100%;
            padding: 14px;
            background: #3b82f6;
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 16px;
            cursor: pointer;
        }

        button:disabled {
            background: #94a3b8;
            cursor: not-allowed;
        }

        #captcha-box {
            margin: 25px 0;
        }

        .hint {
            font-size: 12px;
            color: #64748b;
            margin-top: 5px;
        }
    </style>
   <script src="https://captcha.wangluozhe.com/static/js/vendors.min.js"></script>
   <script src="https://captcha.wangluozhe.com/static/js/wlzshield.min.js"></script>
</head>
<body>
<div class="form-container">
    <h2>注册账号</h2>
    <form id="registerForm">
        <div class="form-group">
            <label>邮箱地址</label>
            <input type="email" id="email" required>
        </div>

        <div class="form-group">
            <label>设置密码</label>
            <input type="password" id="password" required>
        </div>

        <!-- 验证码容器 -->
        <div class="form-group">
            <label>安全验证</label>
            <div id="captcha-box"></div>
            <div class="hint">请将滑块拖动到充电站,按住充电直到变绿后继续拖动</div>
        </div>

        <button type="submit" id="submitBtn" disabled>注册账号</button>
    </form>
</div>

<script>
    let captchaInstance = null;
    let isVerified = false;
    let verificationData = null;

    // 初始化验证码
    captchaInstance = new WlzShield.CheckpointCaptcha({
        siteKey: 'your-site-key',
        containerId: '#captcha-box',
        mode: 'embed',
        width: '100%',
        customTexts: {
            desc: "拖动至<b>蓝色充电站</b>停下充电,变绿后继续"
        },
        customStyles: {
            backgroundColor: '#ffffff',
            border: '1px solid #e2e8f0',
            borderRadius: '8px',
            padding: '15px'
        },
        onSuccess: function (data) {
            console.log('验证成功:', data);
            isVerified = true;
            verificationData = data;
            document.getElementById('submitBtn').disabled = false;

            // 显示成功提示
            const submitBtn = document.getElementById('submitBtn');
            submitBtn.innerHTML = '✓ 验证成功,点击注册';
            submitBtn.style.background = '#10b981';
        },
        onFailure: function (data) {
            console.warn('验证失败:', data);
            isVerified = false;
            document.getElementById('submitBtn').disabled = true;
            document.getElementById('submitBtn').innerHTML = '注册账号';
            document.getElementById('submitBtn').style.background = '#3b82f6';

            // 显示错误提示
            alert('验证失败:' + (data.msg || '请重试'));
        },
        onError: function (error) {
            console.error('系统错误:', error);
            alert('验证系统错误,请刷新页面重试');
        },
        onReady: function () {
            console.log('站点停靠验证码已加载完成');
        }
    });

    // 表单提交处理
    document.getElementById('registerForm').addEventListener('submit', function (e) {
        e.preventDefault();

        if (!isVerified) {
            alert('请先完成安全验证');
            return;
        }

        // 收集表单数据
        const formData = {
            email: document.getElementById('email').value,
            password: document.getElementById('password').value,
            // 传递验证数据给后端
            captchaData: verificationData
        };

        console.log('提交注册数据:', formData);
        // 这里发送到您的后端进行验证

        // 模拟提交
        document.getElementById('submitBtn').innerHTML = '注册中...';
        document.getElementById('submitBtn').disabled = true;

        setTimeout(() => {
            alert('注册成功!');
            // 重置表单和验证码
            document.getElementById('registerForm').reset();
            captchaInstance.reset();
            isVerified = false;
            document.getElementById('submitBtn').innerHTML = '注册账号';
            document.getElementById('submitBtn').disabled = true;
            document.getElementById('submitBtn').style.background = '#3b82f6';
        }, 1000);
    });
</script>
</body>
</html>

示例2:弹窗模式应用

html
<!DOCTYPE html>
<html>
<head>
    <title>弹窗验证示例</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            padding: 40px;
        }

        .comment-box {
            max-width: 600px;
            margin: 0 auto;
        }

        textarea {
            width: 100%;
            height: 100px;
            padding: 12px;
            border: 1px solid #ddd;
            border-radius: 6px;
            margin-bottom: 15px;
        }

        .toolbar {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        button {
            padding: 10px 24px;
            border: none;
            border-radius: 6px;
            cursor: pointer;
        }

        #submitComment {
            background: #3b82f6;
            color: white;
        }

        #submitComment:disabled {
            background: #94a3b8;
            cursor: not-allowed;
        }

        #captcha-trigger {
            background: #f1f5f9;
            color: #475569;
            border: 1px solid #cbd5e1;
        }
    </style>
    <script src="https://captcha.wangluozhe.com/static/js/vendors.min.js"></script>
    <script src="https://captcha.wangluozhe.com/static/js/wlzshield.min.js"></script>
</head>
<body>
<div class="comment-box">
    <h2>发表评论</h2>
    <textarea id="comment" placeholder="请输入您的评论..."></textarea>

    <div class="toolbar">
        <button id="captcha-trigger">验证身份</button>
        <button id="submitComment" disabled>提交评论</button>
    </div>
</div>

<!-- 验证码容器(隐藏,仅用于弹窗模式) -->
<div id="captcha-container" style="display: none;"></div>

<script>
    let captchaInstance = null;
    let isVerified = false;

    // 初始化验证码(弹窗模式)
    captchaInstance = new WlzShield.CheckpointCaptcha({
        siteKey: 'your-site-key',
        containerId: '#captcha-container',
        mode: 'popup',
        customTexts: {
            buttonText: '点击验证',
            popupTitle: '安全验证',
            desc: "拖动至<b>蓝色充电站</b>停下充电,变绿后继续"
        },
        popupStyles: {
            maxWidth: '380px',
            borderRadius: '12px'
        },
        onSuccess: function (data) {
            console.log('验证成功:', data);
            isVerified = true;
            document.getElementById('submitComment').disabled = false;
            document.getElementById('captcha-trigger').innerHTML = '✓ 已验证';
            document.getElementById('captcha-trigger').style.background = '#dcfce7';
            document.getElementById('captcha-trigger').style.color = '#16a34a';
            document.getElementById('captcha-trigger').style.borderColor = '#86efac';
        },
        onFailure: function (data) {
            console.warn('验证失败:', data);
            isVerified = false;
            document.getElementById('submitComment').disabled = true;
            alert('验证失败,请重试');
        },
        onOpen: function () {
            console.log('验证弹窗已打开');
        },
        onClose: function () {
            console.log('验证弹窗已关闭');
        }
    });

    // 触发验证按钮
    document.getElementById('captcha-trigger').addEventListener('click', function () {
        if (!isVerified) {
            // 弹窗模式会自动显示验证窗口
            // 验证成功后会触发onSuccess回调
        } else {
            alert('您已经通过验证');
        }
    });

    // 提交评论
    document.getElementById('submitComment').addEventListener('click', function () {
        if (!isVerified) {
            alert('请先完成身份验证');
            return;
        }

        const comment = document.getElementById('comment').value;
        if (!comment.trim()) {
            alert('请输入评论内容');
            return;
        }

        console.log('提交评论:', comment);
        // 这里发送到后端

        // 清空表单
        document.getElementById('comment').value = '';
        document.getElementById('submitComment').disabled = true;
        document.getElementById('captcha-trigger').innerHTML = '验证身份';
        document.getElementById('captcha-trigger').style.background = '#f1f5f9';
        document.getElementById('captcha-trigger').style.color = '#475569';
        document.getElementById('captcha-trigger').style.borderColor = '#cbd5e1';
        isVerified = false;

        alert('评论提交成功!');
    });
</script>
</body>
</html>

示例3:与Vue.js集成

vue

<template>
  <div class="checkout-page">
    <h2>订单支付</h2>

    <div class="order-summary">
      <p>商品总额:¥{{ totalAmount }}</p>
      <p>运费:¥{{ shippingFee }}</p>
      <p class="total">应付总额:¥{{ totalAmount + shippingFee }}</p>
    </div>

    <!-- 验证码区域 -->
    <div class="captcha-section">
      <h3>安全验证</h3>
      <div ref="captchaContainer" class="captcha-box"></div>
      <p class="hint" v-if="!verified">请完成验证以继续支付</p>
      <p class="hint success" v-else>✓ 验证通过,可以支付</p>
    </div>

    <button
        @click="handlePayment"
        :disabled="!verified"
        :class="{ 'disabled': !verified }"
    >
      {{ verified ? '确认支付' : '请先完成验证' }}
    </button>
  </div>
</template>

<script>
  import {ref, onMounted} from 'vue';

  export default {
    setup() {
      const captchaContainer = ref(null);
      const verified = ref(false);
      const totalAmount = ref(199.99);
      const shippingFee = ref(10.00);
      let captchaInstance = null;

      onMounted(() => {
        if (captchaContainer.value) {
          // 动态设置containerId
          captchaContainer.value.id = 'checkpoint-captcha-' + Date.now();

          captchaInstance = new WlzShield.CheckpointCaptcha({
            siteKey: 'your-site-key',
            containerId: '#' + captchaContainer.value.id,
            mode: 'embed',
            width: '100%',
            customTexts: {
              desc: "拖动滑块到<b>蓝色充电站</b>,按住充电直到变绿"
            },
            onSuccess: (data) => {
              console.log('验证成功:', data);
              verified.value = true;
            },
            onFailure: () => {
              verified.value = false;
            },
            onError: (error) => {
              console.error('验证错误:', error);
              verified.value = false;
            }
          });
        }
      });

      const handlePayment = () => {
        if (!verified.value) {
          alert('请先完成安全验证');
          return;
        }

        // 执行支付逻辑
        console.log('开始支付流程...');
        alert('支付成功!');

        // 重置验证码
        if (captchaInstance) {
          captchaInstance.reset();
          verified.value = false;
        }
      };

      return {
        captchaContainer,
        verified,
        totalAmount,
        shippingFee,
        handlePayment
      };
    }
  };
</script>

<style scoped>
  .checkout-page {
    max-width: 500px;
    margin: 0 auto;
    padding: 30px;
  }

  .order-summary {
    background: #f8fafc;
    padding: 20px;
    border-radius: 8px;
    margin: 20px 0;
  }

  .total {
    font-weight: bold;
    font-size: 18px;
    color: #3b82f6;
  }

  .captcha-section {
    margin: 30px 0;
  }

  .captcha-box {
    margin: 15px 0;
  }

  .hint {
    font-size: 12px;
    color: #64748b;
    margin-top: 8px;
  }

  .hint.success {
    color: #10b981;
  }

  button {
    width: 100%;
    padding: 16px;
    background: #3b82f6;
    color: white;
    border: none;
    border-radius: 8px;
    font-size: 16px;
    cursor: pointer;
  }

  button.disabled {
    background: #94a3b8;
    cursor: not-allowed;
  }
</style>

实例方法

reset()

重置验证码到初始状态。该方法会重新生成随机的"站点位置"和"充电目标时长",并重置UI状态。

javascript
// 1. 保存实例引用
const myCaptcha = new WlzShield.CheckpointCaptcha({
    siteKey: 'your-site-key',
    containerId: '#captcha-box'
});

// 2. 在需要的时候调用
function handleFormError() {
    // 重置验证码
    myCaptcha.reset();

    // 重置表单状态
    document.getElementById('submitBtn').disabled = true;
    document.getElementById('submitBtn').innerText = '提交';
}

openModal()closeModal()

仅适用于popup模式:手动打开或关闭验证弹窗。

javascript
const popupCaptcha = new WlzShield.CheckpointCaptcha({
    siteKey: 'your-site-key',
    containerId: '#captcha-box',
    mode: 'popup'
});

// 手动打开弹窗
document.getElementById('custom-trigger').addEventListener('click', () => {
    popupCaptcha.openModal();
});

// 手动关闭弹窗
document.getElementById('cancel-btn').addEventListener('click', () => {
    popupCaptcha.closeModal();
});

事件回调详解

onSuccess

验证成功时触发,返回后端验证数据。

javascript
new WlzShield.CheckpointCaptcha({
    // ... 其他配置
    onSuccess: function (data) {
        console.log('验证成功,完整响应:', data);

        // 存储验证凭证
        localStorage.setItem('wsToken', data.wsToken || '');
    }
});

onFailure

验证失败时触发,可能是充电时间不足、过长或轨迹异常。

javascript
new WlzShield.CheckpointCaptcha({
    // ... 其他配置
    onFailure: function (data) {
        console.warn('验证失败,原因:', data.msg || '未知原因');

        // 根据错误类型提供不同的用户提示
        if (data.msg?.includes('充电时间')) {
            alert('充电时间不符合要求,请重试');
        } else if (data.msg?.includes('轨迹')) {
            alert('操作轨迹异常,请正常拖动滑块');
        } else {
            alert('验证失败,请重试');
        }

        // 自动重置验证码
        setTimeout(() => {
            this.reset();
        }, 1500);
    }
});

onError

系统错误时触发,通常是网络问题或配置错误。

javascript
new WlzShield.CheckpointCaptcha({
    // ... 其他配置
    onError: function (error) {
        console.error('验证系统错误:', error);

        // 根据错误类型提供不同的处理
        if (error.message.includes('Network')) {
            alert('网络连接失败,请检查网络设置');
        } else if (error.message.includes('Timeout')) {
            alert('请求超时,请重试');
        } else if (error.message.includes('siteKey')) {
            alert('验证配置错误,请联系网站管理员');
        } else {
            alert('验证系统异常,请刷新页面');
        }
    }
});

onReady, onOpen, onClose

组件生命周期回调:

javascript
new WlzShield.CheckpointCaptcha({
    // ... 其他配置
    onReady: function () {
        console.log('验证码组件已加载完成,可以开始验证');
        // 可以在这里显示加载完成的提示
    },

    onOpen: function () {
        console.log('验证弹窗已打开');
        // 弹窗打开时,可以暂停页面上的其他交互
        document.body.style.overflow = 'hidden';
    },

    onClose: function () {
        console.log('验证弹窗已关闭');
        // 弹窗关闭时,恢复页面交互
        document.body.style.overflow = '';

        // 如果未验证成功,显示提示
        if (!this.isVerified) {
            alert('验证未完成,请完成验证以继续');
        }
    }
});

注意事项

  1. 随机参数生成:每次验证会随机生成站点位置(20-80%)和充电时间(800-2000ms),确保每次验证都不同。

  2. 物理阻挡机制:未充满电时,滑块无法越过站点右侧2%的位置,提供真实的物理反馈。

  3. 移动端适配:组件完全支持触摸操作,在移动设备上表现良好。

  4. 性能优化:充电动画使用requestAnimationFrame实现,确保流畅的60fps动画效果。

  5. 安全特性:自动启用鼠标轨迹追踪,采集用户操作行为数据用于人机识别。

  6. 多实例支持:可以在同一页面创建多个CheckpointCaptcha实例,每个实例独立工作。

故障排查

常见问题

  1. 滑块无法拖动

    • 检查容器是否正确初始化
    • 查看控制台是否有JavaScript错误
    • 确保没有其他JavaScript库干扰事件监听
  2. 充电不启动

    • 确保滑块在站点±3%范围内
    • 检查是否已充满电(站点变绿)
    • 查看是否有控制台警告
  3. 验证总是失败

    • 确认siteKey是否正确
    • 检查网络连接,确保能访问验证接口
    • 查看后端验证服务是否正常运行
  4. 弹窗模式不显示

    • 确认mode设置为'popup'
    • 检查容器是否隐藏(弹窗模式容器通常隐藏)
    • 确保没有CSS样式覆盖弹窗显示

调试技巧

javascript
// 创建实例时捕获错误
try {
    const captcha = new WlzShield.CheckpointCaptcha({
        // 配置
    });
} catch (error) {
    console.error('初始化失败:', error);
    // 显示友好的错误提示
    document.getElementById('captcha-container').innerHTML =
        '<div class="error">验证码加载失败,请刷新页面或联系管理员</div>';
}

// 监听所有可能的事件
const captcha = new WlzShield.CheckpointCaptcha({
    // ... 配置
    onReady: () => console.log('组件就绪'),
    onOpen: () => console.log('弹窗打开'),
    onClose: () => console.log('弹窗关闭'),
    onSuccess: (data) => console.log('验证成功', data),
    onFailure: (data) => console.log('验证失败', data),
    onError: (error) => console.log('系统错误', error)
});

浏览器兼容性

  • Chrome 60+
  • Firefox 63+
  • Safari 12+
  • Edge 79+
  • iOS Safari 12+
  • Android Chrome 60+

组件使用现代Web API,对于不支持某些API的旧浏览器会自动降级处理。


通过以上详细文档,您可以全面了解CheckpointCaptcha的使用方法和配置选项。站点停靠验证提供了独特的按压蓄力交互,在保证用户体验的同时提供了强大的安全防护能力。如有进一步问题,请参考源码或联系技术支持。

Released under the MIT License.