Skip to content

磁力排斥验证 (MagnetCaptcha)

趣味性物理交互组件。滑块带有"高压电"设定,用户不能直接触碰滑块,必须利用鼠标/手指在滑块周围产生的"斥力场",将其隔空推动到终点。这种独特的交互方式能有效采集用户的细微操作行为,提高人机识别准确率。

交互流程

  1. 场景中出现一个紫色方块(滑块)和绿色终点区域。
  2. 用户移动鼠标靠近滑块,滑块会因"同极相斥"原理向相反方向移动。
  3. 切勿直接接触滑块,否则会触发"短路"导致失败。
  4. 利用斥力将滑块推入右侧的绿色终点区域。

基础用法

html

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

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

    // 可选:监听验证结果
    magnet.config.onSuccess = function (data) {
        console.log('验证成功:', data);
        // 将验证凭证提交到您的后端
        // data.wsToken 可用于后续请求的验证
    };
</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可选请求超时时间(毫秒)
forceStrengthnumber内部计算可选磁力场强度系数,影响斥力大小

高级配置

参数名类型默认值说明
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, 网络断开)

详细配置说明

磁力强度配置 (forceStrength)

您可以调整磁力场的强度:

javascript
const magnet = new WlzShield.MagnetCaptcha({
    siteKey: 'your-site-key',
    containerId: '#captcha-container',
    forceStrength: 0.6, // 默认约为0.45,增大使磁力更强
});

// 注意:过大的力可能导致滑块移动过快,难以控制
// 过小的力可能导致滑块移动过慢,用户体验差

自定义文案 (customTexts)

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

javascript
const magnet = new WlzShield.MagnetCaptcha({
    siteKey: 'your-site-key',
    containerId: '#captcha-container',
    customTexts: {
        // 覆盖描述文本(支持HTML)
        desc: "⚠️ <b>高压危险</b>:利用磁场斥力推动滑块",
        // 覆盖失败文本
        fail: "短路!请重试",
        // 覆盖成功文本
        success: "✅ 验证通过",
        // 覆盖重试文本
        retry: "点击重试",
        // 弹窗模式下的按钮文本(仅popup模式有效)
        buttonText: "磁力验证",
        // 弹窗标题(仅popup模式有效)
        popupTitle: "安全验证"
    }
});

自定义样式 (customStyles)

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

javascript
const magnet = new WlzShield.MagnetCaptcha({
    siteKey: 'your-site-key',
    containerId: '#captcha-container',
    mode: 'embed',
    customStyles: {
        // 修改背景色
        backgroundColor: '#f8fafc',
        // 添加边框
        border: '1px solid #e2e8f0',
        // 调整圆角
        borderRadius: '12px',
        // 调整内边距
        padding: '20px',
        // 修改字体
        fontFamily: 'system-ui, -apple-system, sans-serif'
    }
});

滑块样式自定义

磁力滑块使用CSS自定义,您可以通过注入额外样式来修改:

css
/* 自定义磁力滑块样式 */
.magnet-block {
    background: linear-gradient(135deg, #8b5cf6, #6366f1) !important;
    box-shadow: 0 6px 12px rgba(99, 102, 241, 0.3) !important;
    border: 2px solid white !important;
}

/* 鼠标指针样式 */
.mouse-cursor {
    border-color: #f59e0b !important;
    background: rgba(245, 158, 11, 0.1) !important;
}

/* 终点区域样式 */
.goal-area {
    background: linear-gradient(90deg, transparent, rgba(34, 197, 94, 0.3)) !important;
}

完整示例

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

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

        .contact-form {
            background: #f8fafc;
            padding: 30px;
            border-radius: 12px;
        }

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

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

        input, textarea {
            width: 100%;
            padding: 12px;
            border: 1px solid #cbd5e1;
            border-radius: 6px;
        }

        button {
            padding: 12px 24px;
            background: #3b82f6;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
        }

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

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

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

        .warning {
            color: #dc2626;
            font-weight: bold;
        }
    </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="contact-form">
    <h2>联系我们</h2>
    <form id="contactForm">
        <div class="form-group">
            <label>姓名</label>
            <input type="text" id="name" required>
        </div>

        <div class="form-group">
            <label>邮箱</label>
            <input type="email" id="email" required>
        </div>

        <div class="form-group">
            <label>消息</label>
            <textarea id="message" rows="4" required></textarea>
        </div>

        <div class="form-group">
            <label>安全验证 <span class="warning">⚠️ 请勿直接触碰滑块</span></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 wsToken = '';

    // 初始化验证码
    captchaInstance = new WlzShield.MagnetCaptcha({
        siteKey: 'your-site-key',
        containerId: '#captcha-box',
        mode: 'embed',
        width: '100%',
        customTexts: {
            desc: "⚠️ <b>高压危险</b>:在滑块周围利用<b>斥力</b>将其推至终点"
        },
        onSuccess: function (data) {
            console.log('验证成功,服务器响应:', data);
            isVerified = true;
            wsToken = data.wsToken || '';
            document.getElementById('submitBtn').disabled = false;
            document.getElementById('submitBtn').innerHTML = '✓ 已验证,点击发送';
        },
        onFailure: function (data) {
            console.warn('验证失败:', data);
            isVerified = false;
            wsToken = '';
            document.getElementById('submitBtn').disabled = true;
            document.getElementById('submitBtn').innerHTML = '发送消息';

            // 显示错误信息
            const errorMsg = data.msg === 'Short Circuit!' ? '接触滑块导致短路!请重试' : (data.msg || '验证失败');
            alert(errorMsg);
        },
        onError: function (error) {
            console.error('系统错误:', error);
            alert('验证系统错误,请刷新页面重试');
        }
    });

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

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

        // 收集表单数据
        const formData = {
            name: document.getElementById('name').value,
            email: document.getElementById('email').value,
            message: document.getElementById('message').value,
            wsToken: wsToken, // 包含验证凭证
            timestamp: Date.now()
        };

        console.log('提交联系数据:', formData);

        // 这里发送到您的后端进行验证
        // 后端应使用wsToken验证请求的合法性

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

        setTimeout(() => {
            alert('消息发送成功!我们会尽快回复您。');
            // 重置表单和验证码
            document.getElementById('contactForm').reset();
            captchaInstance.reset();
            isVerified = false;
            wsToken = '';
            document.getElementById('submitBtn').innerHTML = '发送消息';
            document.getElementById('submitBtn').disabled = true;
        }, 1000);
    });
</script>
</body>
</html>

示例2:弹窗模式应用

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

        .payment-form {
            max-width: 500px;
            margin: 0 auto;
        }

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

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

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

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

        #captcha-trigger {
            background: #f1f5f9;
            color: #475569;
            border: 1px solid #cbd5e1;
            padding: 10px 20px;
            border-radius: 6px;
            cursor: pointer;
        }

        #submitPayment {
            background: #3b82f6;
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 6px;
            cursor: pointer;
        }

        #submitPayment:disabled {
            background: #94a3b8;
            cursor: not-allowed;
        }
    </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="payment-form">
    <h2>支付订单</h2>

    <div class="form-group">
        <label>金额</label>
        <input type="text" id="amount" value="¥199.00" readonly>
    </div>

    <div class="form-group">
        <label>银行卡号</label>
        <input type="text" id="cardNumber" placeholder="**** **** **** ****">
    </div>

    <div class="toolbar">
        <div id="captcha-trigger">安全验证</div>
        <button id="submitPayment" disabled>确认支付</button>
    </div>
</div>

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

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

    // 初始化验证码(弹窗模式)
    captchaInstance = new WlzShield.MagnetCaptcha({
        siteKey: 'your-site-key',
        containerId: '#captcha-container',
        mode: 'popup',
        customTexts: {
            buttonText: '磁力验证',
            popupTitle: '安全验证',
            desc: "⚠️ <b>高压危险</b>:利用磁场斥力推动滑块"
        },
        onSuccess: function (data) {
            console.log('验证成功:', data);
            isVerified = true;
            wsToken = data.wsToken || '';
            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';
            document.getElementById('submitPayment').disabled = false;
        },
        onFailure: function (data) {
            console.warn('验证失败:', data);
            isVerified = false;
            wsToken = '';
            alert('验证失败: ' + (data.msg || '请重试'));
        },
        onOpen: function () {
            console.log('验证弹窗已打开');
        },
        onClose: function () {
            console.log('验证弹窗已关闭');
        }
    });

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

    // 提交支付
    document.getElementById('submitPayment').addEventListener('click', function () {
        if (!isVerified) {
            alert('请先完成安全验证');
            return;
        }

        const cardNumber = document.getElementById('cardNumber').value;

        if (!cardNumber.trim() || cardNumber.length < 16) {
            alert('请输入有效的银行卡号');
            return;
        }

        // 构建请求数据
        const paymentData = {
            amount: 199.00,
            cardNumber: cardNumber,
            wsToken: wsToken
        };

        console.log('支付请求:', paymentData);

        // 发送到后端,后端应验证wsToken

        // 清空表单
        document.getElementById('cardNumber').value = '';
        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';
        document.getElementById('submitPayment').disabled = true;
        isVerified = false;
        wsToken = '';

        alert('支付成功!');
    });
</script>
</body>
</html>

示例3:移动端优化

javascript
// 移动端专用配置,优化触摸体验
const mobileMagnet = new WlzShield.MagnetCaptcha({
    siteKey: 'your-site-key',
    containerId: '#mobile-captcha',
    mode: 'embed',
    width: '100%',
    forceStrength: 0.5, // 移动端略微降低力场强度,便于控制
    customTexts: {
        desc: "⚠️ <b>高压危险</b>:用手指靠近滑块,利用斥力推动"
    },
    customStyles: {
        // 移动端增大容器高度,便于操作
        height: '180px'
    },
    onSuccess: function (data) {
        console.log('移动端验证成功:', data);
        // 移动端特殊处理
        if (window.innerWidth <= 768) {
            alert('验证成功!');
        }
    },
    onFailure: function (data) {
        console.warn('验证失败:', data);
        // 移动端友好提示
        alert('请勿触碰滑块,用手指在周围推动');
    }
});

实例方法

reset()

重置验证码到初始状态。该方法会重新生成随机的滑块起始位置,并重置UI状态。

javascript
// 1. 保存实例引用
const myCaptcha = new WlzShield.MagnetCaptcha({
    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.MagnetCaptcha({
    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.MagnetCaptcha({
    // ... 其他配置
    onSuccess: function (data) {
        // 服务器返回的数据结构
        // {
        //   "status": true,
        //   "msg": "认证成功",
        //   "wsToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
        // }

        console.log('验证成功,服务器响应:', data);

        // 获取wsToken
        const wsToken = data.wsToken;

        // 将wsToken存储到隐藏字段或全局变量中
        document.getElementById('captcha-token').value = wsToken;
    }
});

onFailure

验证失败时触发,返回服务器响应数据。注意:wsToken字段为空字符串

javascript
new WlzShield.MagnetCaptcha({
    // ... 其他配置
    onFailure: function (data) {
        // 服务器返回的数据结构
        // {
        //   "status": false,
        //   "msg": "轨迹异常或接触滑块",
        //   "wsToken": ""
        // }

        console.warn('验证失败,服务器响应:', data);

        // wsToken为空字符串
        const wsToken = data.wsToken; // 空字符串

        // 显示错误信息给用户
        const errorMsg = data.msg || '验证失败,请重试';

        // 根据错误类型提供不同的处理
        if (data.msg?.includes('接触') || data.msg?.includes('Short Circuit')) {
            alert('⚠️ 警告:直接接触滑块导致短路!请保持距离推动');
        } else if (data.msg?.includes('轨迹')) {
            alert('操作轨迹异常,请正常操作');
        } else {
            alert(errorMsg);
        }

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

onError

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

javascript
new WlzShield.MagnetCaptcha({
    // ... 其他配置
    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.MagnetCaptcha({
    // ... 其他配置
    onReady: function () {
        console.log('磁力排斥验证码已加载完成,可以开始验证');
        // 可以在这里显示加载完成的提示或启用界面元素
    },

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

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

        // 如果未验证成功,显示提示
        if (!this.isVerified) {
            console.log('验证未完成,需要继续验证');
        }
    }
});

注意事项

  1. 随机位置生成:每次验证会随机生成滑块起始位置(10-30% X, 20-80% Y),确保每次验证都不同。

  2. 物理引擎模拟:使用2D向量计算实现真实的磁力排斥效果,距离越近斥力越强。

  3. 防作弊机制:组件记录鼠标移动轨迹和滑块运动轨迹,用于后端的人机分析。

  4. 接触检测:鼠标/手指与滑块距离过近(❤️.5%)会触发"短路"失败,防止直接拖动。

  5. 性能优化:使用requestAnimationFrame实现流畅的60fps动画,避免性能问题。

  6. 移动端适配:完全支持触摸操作,优化了移动端的手感和交互。

故障排查

常见问题

  1. 滑块不移动或移动异常

    • 检查鼠标/手指是否足够靠近滑块(距离<18%)
    • 确认没有直接接触滑块(距离>3.5%)
    • 查看控制台是否有JavaScript错误
  2. 鼠标指针不显示

    • 检查容器CSS是否正确设置
    • 确认没有其他样式覆盖指针显示
    • 在移动端确保触摸事件正常
  3. 验证总是失败

    • 确认siteKey是否正确
    • 检查网络连接,确保能访问验证接口
    • 查看后端验证服务是否正常运行
    • 避免直接接触滑块导致短路
  4. 弹窗模式不显示

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

调试技巧

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

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

// 检查位置数据
console.log('滑块位置范围: X(5-95%), Y(10-90%)');
console.log('磁力感应范围: 18%');
console.log('接触阈值: 3.5%');
console.log('成功阈值: X > 92%');

浏览器兼容性

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

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


通过以上详细文档,您可以全面了解MagnetCaptcha的使用方法和配置选项。磁力排斥验证提供了独特的物理交互体验,在保证趣味性的同时提供了强大的安全防护能力。如有进一步问题,请参考源码或联系技术支持。

Released under the MIT License.