如何在HTML Canvas鼠标移动时实时显示绘制的矩形
实现Canvas实时拖动绘制矩形(保留多矩形功能)
问题分析
现有代码存在三个核心问题:
drawSquare函数中宽高计算逻辑错误,误用了画布的偏移量而非当前鼠标坐标- 拖动过程中未清空画布,导致临时矩形重复绘制出现拖影
- 未保存已完成的矩形,无法在重绘时保留之前的绘制结果
解决方案
核心思路是维护已完成矩形的列表,通过"清空画布→重绘历史矩形→绘制临时矩形"的流程实现实时预览:
- 用数组保存所有绘制完成的矩形
- 鼠标拖动时,每次先清空画布,再重绘所有历史矩形,最后绘制当前拖动的临时矩形
- 鼠标松开时,将当前矩形存入历史列表,完成绘制
修改后的完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>实时绘制矩形</title> <style> #heatmapContainer { border: 1px solid red; } </style> </head> <body> <div class="heatmapWrapper"> <div id="heatmapContainer" style="height: 4205px; width: 1278px"> <div id="heatmap1" class="heatmapTile" style="height: 4205px; position: relative"> <canvas id="myCanvas" class="heatmap-canvas" width="1278" height="4205" style="position: absolute; left: 0px; top: 0px"></canvas> </div> </div> </div> <script> var canvas = document.getElementById('myCanvas'), context = canvas.getContext('2d'); // 保存已完成的矩形列表 const completedRects = []; let startPos = null; let isDragging = false; // 获取Canvas上的鼠标坐标(适配页面缩放) function getMousePos(canvas, evt) { const rect = canvas.getBoundingClientRect(); const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; return { x: (evt.clientX - rect.left) * scaleX, y: (evt.clientY - rect.top) * scaleY }; } // 绘制所有已完成的矩形 function drawCompletedRects() { context.strokeStyle = 'blue'; completedRects.forEach(rect => { context.strokeRect(rect.x, rect.y, rect.width, rect.height); }); } // 鼠标按下:记录起始位置 function startDrag(e) { startPos = getMousePos(canvas, e); isDragging = true; } // 鼠标松开/移出:保存当前矩形到已完成列表 function endDrag(e) { if (!isDragging || !startPos) return; const endPos = getMousePos(canvas, e); const rect = { x: Math.min(startPos.x, endPos.x), y: Math.min(startPos.y, endPos.y), width: Math.abs(endPos.x - startPos.x), height: Math.abs(endPos.y - startPos.y) }; // 过滤掉宽高为0的无效矩形 if (rect.width > 0 && rect.height > 0) { completedRects.push(rect); } isDragging = false; // 最后重绘一次,确保临时矩形消失,只保留已完成的 context.clearRect(0, 0, canvas.width, canvas.height); drawCompletedRects(); } // 鼠标移动:实时绘制临时矩形 function drawTempRect(e) { if (!isDragging || !startPos) return; const currentPos = getMousePos(canvas, e); // 清空画布 context.clearRect(0, 0, canvas.width, canvas.height); // 先画已完成的矩形 drawCompletedRects(); // 计算临时矩形的坐标(适配拖拽方向,确保矩形始终可见) const x = Math.min(startPos.x, currentPos.x); const y = Math.min(startPos.y, currentPos.y); const width = Math.abs(currentPos.x - startPos.x); const height = Math.abs(currentPos.y - startPos.y); // 绘制临时矩形 context.strokeStyle = 'blue'; context.strokeRect(x, y, width, height); } // 绑定事件 canvas.addEventListener('mousedown', startDrag); canvas.addEventListener('mouseup', endDrag); canvas.addEventListener('mousemove', drawTempRect); // 防止鼠标移出Canvas后松开无法结束拖拽 canvas.addEventListener('mouseleave', endDrag); </script> </body> </html>
关键修改点说明
- 历史矩形存储:用
completedRects数组保存所有绘制完成的矩形,保证重绘时不会丢失之前的内容 - 实时预览逻辑:每次鼠标移动都先清空画布,再重绘历史矩形,最后绘制临时矩形,既实现实时预览又保留多矩形功能
- 坐标适配:正确计算临时矩形的位置和尺寸,支持从任意方向拖动绘制矩形
- 边界处理:添加
mouseleave事件处理鼠标移出画布的场景,同时过滤无效的零尺寸矩形
内容的提问来源于stack exchange,提问作者Moro Owusu Afriyie
相关产品推荐
相关产品推荐

