直接用 dragstart 会失效,因为 <img> 等元素默认不可拖拽,须设 draggable="true";且 dragstart 中需调用 event.dataTransfer.setData(),dragover 中须同时 preventDefault() 和设置 dropEffect。

为什么直接用 dragstart 会失效?
拼图验证码的核心是“拖动缺块到正确位置”,但很多初学者一上来就监听 dragstart,结果发现事件根本不触发——因为默认情况下,<img> 和大多数非表单元素不支持原生拖放,必须显式设置 draggable="true",且需阻止默认行为干扰定位逻辑。
-
<div class="puzzle-block" draggable="true">是必要前提,缺了这句,后续所有拖放监听都收不到事件 - 在
dragstart回调里必须调用event.dataTransfer.setData('text/plain', '')(哪怕只设空字符串),否则 Chrome/Firefox 会静默禁用拖拽 - 别在
dragover里只写event.preventDefault()就完事:漏掉它,drop永远不会触发;但只写它又不够,得配合event.dataTransfer.dropEffect = 'move'明确意图,否则 Safari 下可能无响应
如何让滑块精准吸附到缺口边缘?
用户拖动时,滑块常“飘”过头或停不准,根本原因是没做坐标归一化和容差判断。缺口位置是静态的(比如 left: 120px),但拖动中获取的 clientX 是全局坐标,需减去容器偏移再比对。
- 用
getBoundingClientRect()获取拼图容器左上角相对于视口的位置,每次drag时实时计算:const x = event.clientX - containerRect.left - 吸附阈值建议设为
8px(太小用户难触发,太大易误判),即Math.abs(x - targetX) 才视为到位 - 到位后别立刻判定成功:加个
setTimeout延迟 100ms 再检查,避免抖动误触;同时把滑块transform: translateX(...)强制吸过去,给用户视觉反馈
怎么防止用户绕过前端校验?
纯前端拼图逻辑可被禁用 JS、篡改 DOM 或直接调接口绕过,服务端必须重新验证。关键不是“用户拖没拖”,而是“拖动轨迹是否符合人类行为特征”。
- 前端需上报原始轨迹数据:
timestamp、x、y序列(至少 5 点),而非只传“是否完成”布尔值 - 服务端校验三要素:
总耗时 < 300ms(机器秒解)、点数 < 3(直线拖到底)、最大速度 > 200px/ms(物理不可能)——任一命中即拒 - 缺口位置不能写死在 HTML 里,要由后端动态生成并返回
token+offset_x加密值,前端仅用于渲染,校验时以服务端为准
移动端 touch 事件怎么和 drag 逻辑共存?
PC 端用 drag 系列事件很顺,但手机上 touchstart/touchmove 是唯一选择,强行套用 draggable 会导致 iOS Safari 白屏或卡顿。
立即学习“前端免费学习笔记(深入)”;
- 检测
'ontouchstart' in window,真机环境直接禁用draggable,改用touchstart绑定,通过event.touches[0].clientX取坐标 - touchmove 必须加
{ passive: false },否则 Chrome 会静默忽略preventDefault(),导致无法阻止页面滚动 - 移动端没有
drop,到位判断逻辑要复用到touchend:记录最后一次 touchmove 的坐标,touchend时立即比对,避免抬起延迟
真正难的不是画出拼图,而是让每一次拖动都留下可验证的行为指纹;服务端那个 offset_x 加密值,千万别用 base64 硬编码在前端 JS 里——那和把钥匙挂门把手上没区别。


















