You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在HTML Canvas鼠标移动时实时显示绘制的矩形

实现Canvas实时拖动绘制矩形(保留多矩形功能)

问题分析

现有代码存在三个核心问题:

  • drawSquare函数中宽高计算逻辑错误,误用了画布的偏移量而非当前鼠标坐标
  • 拖动过程中未清空画布,导致临时矩形重复绘制出现拖影
  • 未保存已完成的矩形,无法在重绘时保留之前的绘制结果

解决方案

核心思路是维护已完成矩形的列表,通过"清空画布→重绘历史矩形→绘制临时矩形"的流程实现实时预览:

  1. 用数组保存所有绘制完成的矩形
  2. 鼠标拖动时,每次先清空画布,再重绘所有历史矩形,最后绘制当前拖动的临时矩形
  3. 鼠标松开时,将当前矩形存入历史列表,完成绘制

修改后的完整代码

<!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>

关键修改点说明

  1. 历史矩形存储:用completedRects数组保存所有绘制完成的矩形,保证重绘时不会丢失之前的内容
  2. 实时预览逻辑:每次鼠标移动都先清空画布,再重绘历史矩形,最后绘制临时矩形,既实现实时预览又保留多矩形功能
  3. 坐标适配:正确计算临时矩形的位置和尺寸,支持从任意方向拖动绘制矩形
  4. 边界处理:添加mouseleave事件处理鼠标移出画布的场景,同时过滤无效的零尺寸矩形

内容的提问来源于stack exchange,提问作者Moro Owusu Afriyie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 01:16:11