如何实现拖拽蓝色div松开鼠标自动吸附到Canvas高亮方格中心?
拖拽吸附方格效果实现方案
首先明确:原生Path2D没有提供公开API可以直接读取路径内的坐标点,完全没必要花精力逆向解析Path2D对象。你在绘制每个方格的时候本身就已经拿到了方格的绘制坐标,直接把坐标和Path2D对象存在一起是成本最低、逻辑最清晰的实现方式,具体改法如下:
1. 改造方格存储结构
原来的boxes数组只存Path2D实例,改成存储包含路径、坐标、中心点信息的对象,提前算好每个格子的中心点坐标,吸附时直接调用不用重复计算:
let boxes = []; // 修改drawGrid中生成方格的逻辑 if(rowcount%2==0){ for (let boxcount = 0; boxcount < amountOfHorBoxes; boxcount++) { let box = new Path2D(); box.rect(addHorBox, addVertBox, boxsize, boxsize); // 存储路径+位置信息 boxes.push({ path: box, x: addHorBox, y: addVertBox, centerX: addHorBox + boxsize / 2, centerY: addVertBox + boxsize / 2 }); ctxB.fill(box); ctxB.stroke(box); addHorBox += boxsize; } }
2. 调整碰撞检测逻辑
原来遍历boxes时直接传入Path2D做命中判断,现在取对象内的path属性做检测即可,命中后存储整个方格对象,不要只存路径实例:
function moveMouse(e){ e = e || window.event; e.preventDefault(); unit.style.top = (unit.offsetTop + e.clientY - initialY) + "px"; unit.style.left = (unit.offsetLeft + e.clientX - initialX) + "px"; let currentHitTile = null; boxes.forEach(boxItem => { if (ctxB.isPointInPath(boxItem.path, unit.offsetLeft + checkX, unit.offsetTop + checkY)) { ctxB.lineWidth = 2; ctxB.fillStyle = 'green'; ctxB.fill(boxItem.path); ctxB.stroke(boxItem.path); currentHitTile = boxItem; }else{ ctxB.lineWidth = 1; ctxB.strokeStyle = "black"; ctxB.fillStyle = 'white'; ctxB.fill(boxItem.path); ctxB.stroke(boxItem.path); } }); tile = currentHitTile; initialX = e.clientX; initialY = e.clientY; }
3. 补全鼠标抬起时的吸附逻辑
原来的mouseUp是空函数,在这里直接把蓝色元素的位置对齐到命中方格的中心即可,注意要减去蓝色元素自身宽高的一半,保证元素中心和方格中心重合:
function mouseUp(){ document.onmousemove = null; document.onmouseup = null; if(!tile) return; // 对齐时要算上canvas本身的页面偏移,避免位置错位 const boardRect = board.getBoundingClientRect(); unit.style.left = `${tile.centerX - unit.offsetWidth/2 + boardRect.left - window.scrollX}px`; unit.style.top = `${tile.centerY - unit.offsetHeight/2 + boardRect.top - window.scrollY}px`; }
顺手修复的原有问题
- 原HTML代码中canvas标签后多写了一个多余的
</div>闭合标签,会导致布局异常,直接删除即可。 - 原代码鼠标抬起时只清空了
onmousemove事件,没有清空onmouseup,会导致事件残留,上面的代码已经补上。
内容的提问来源于stack exchange,提问作者Justafknquestion
相关产品推荐
相关产品推荐

