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

如何在Canvas上通过鼠标绘制平行四边形?附矩形实现参考代码

实现Canvas上鼠标绘制平行四边形的方案

当然可以实现啦!你当前的代码是通过创建绝对定位的div来模拟矩形绘制,要改成平行四边形有两种可行思路:一种是基于你现有代码用CSS变形改造,另一种是真正利用Canvas的原生绘图API来实现——后者更贴合Canvas的使用场景,我会分别详细说明。


方案一:基于现有div结构+CSS变形改造

你的现有代码是靠div模拟矩形,我们可以通过CSS的transform: skew()属性把矩形扭成平行四边形。这里我调整了交互逻辑,让拖动时同时控制平行四边形的宽高和倾斜角度,尽可能贴合你原来的点击-拖动-完成的操作习惯:

<div id="canvas"></div>

<style>
#canvas {
  width: 2000px;
  height: 2000px;
  border: 10px solid transparent;
  position: relative; /* 确保子元素绝对定位以它为基准 */
}
.parallelogram {
  border: 1px solid #FF0000;
  position: absolute;
  background-color: rgba(255,0,0,0.1); /* 加个半透背景方便看清形状 */
}
</style>

<script>
initDraw(document.getElementById('canvas'));
function initDraw(canvas) {
  function setMousePosition(e) {
    var ev = e || window.event;
    if (ev.pageX) {
      mouse.x = ev.pageX + window.pageXOffset;
      mouse.y = ev.pageY + window.pageYOffset;
    } else if (ev.clientX) {
      mouse.x = ev.clientX + document.body.scrollLeft;
      mouse.y = ev.clientY + document.body.scrollTop;
    }
    // 转换为相对于canvas内部的坐标
    const rect = canvas.getBoundingClientRect();
    mouse.relativeX = mouse.x - rect.left;
    mouse.relativeY = mouse.y - rect.top;
  };

  var mouse = { x: 0, y: 0, startX: 0, startY: 0, relativeX:0, relativeY:0 };
  var element = null;

  canvas.onmousemove = function(e) {
    setMousePosition(e);
    if (element !== null) {
      const width = Math.abs(mouse.relativeX - mouse.startX);
      const height = Math.abs(mouse.relativeY - mouse.startY);
      // 计算水平倾斜角度,根据鼠标拖动的方向动态调整
      const skewAngle = Math.atan2(mouse.relativeY - mouse.startY, mouse.relativeX - mouse.startX) * (180/Math.PI) - 90;
      
      // 把元素定位在拖动区域的左上角
      element.style.left = Math.min(mouse.startX, mouse.relativeX) + 'px';
      element.style.top = Math.min(mouse.startY, mouse.relativeY) + 'px';
      element.style.width = width + 'px';
      element.style.height = height + 'px';
      // 应用倾斜变换
      element.style.transform = `skewX(${skewAngle}deg)`;
    }
  }

  canvas.onclick = function(e) {
    if (element !== null) {
      element = null;
      canvas.style.cursor = "default";
      console.log("绘制完成!");
    } else {
      console.log("开始绘制!");
      setMousePosition(e);
      mouse.startX = mouse.relativeX;
      mouse.startY = mouse.relativeY;
      element = document.createElement('div');
      element.className = 'parallelogram';
      element.style.left = mouse.startX + 'px';
      element.style.top = mouse.startY + 'px';
      canvas.appendChild(element);
      canvas.style.cursor = "crosshair";
    }
  }
}
</script>

这个方案的核心是用transform: skewX()将矩形扭曲成平行四边形,通过鼠标拖动的方向计算倾斜角度,完美适配你原来的交互逻辑。


方案二:原生Canvas绘图API实现(更推荐)

其实你现在的代码并没有真正用到Canvas的绘图能力,只是把它当作了一个容器。原生Canvas的beginPath()、lineTo()等API可以直接绘制任意多边形,包括平行四边形——这种方案更高效,也更符合Canvas的设计初衷,扩展性也更强。

下面是具体实现代码:

<canvas id="canvas" width="2000" height="2000"></canvas>

<style>
#canvas {
  border: 10px solid transparent;
  cursor: default;
}
</style>

<script>
initDraw(document.getElementById('canvas'));
function initDraw(canvas) {
  const ctx = canvas.getContext('2d');
  let isDrawing = false;
  let startPoint = { x: 0, y: 0 };
  let currentPoint = { x: 0, y: 0 };

  // 获取鼠标相对于Canvas内部的坐标
  function getMousePos(e) {
    const rect = canvas.getBoundingClientRect();
    return {
      x: e.clientX - rect.left,
      y: e.clientY - rect.top
    };
  }

  // 绘制平行四边形的核心函数
  function drawParallelogram() {
    ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除画布上的临时图形
    ctx.beginPath();
    // 定义平行四边形的四个顶点:起点 → 右侧顶点 → 右下角顶点 → 左下角顶点 → 闭合
    const width = currentPoint.x - startPoint.x;
    const height = currentPoint.y - startPoint.y;
    // 这里的0.5是倾斜系数,你可以调整这个值来改变平行四边形的倾斜程度
    const skewOffset = height * 0.5;

    ctx.moveTo(startPoint.x, startPoint.y);
    ctx.lineTo(startPoint.x + width, startPoint.y);
    ctx.lineTo(startPoint.x + width + skewOffset, startPoint.y + height);
    ctx.lineTo(startPoint.x + skewOffset, startPoint.y + height);
    ctx.closePath();

    ctx.strokeStyle = '#FF0000';
    ctx.lineWidth = 1;
    ctx.stroke();
    ctx.fillStyle = 'rgba(255,0,0,0.1)';
    ctx.fill();
  }

  canvas.onmousedown = function(e) {
    isDrawing = true;
    startPoint = getMousePos(e);
    currentPoint = startPoint;
    canvas.style.cursor = "crosshair";
  }

  canvas.onmousemove = function(e) {
    if (!isDrawing) return;
    currentPoint = getMousePos(e);
    drawParallelogram();
  }

  canvas.onmouseup = function(e) {
    isDrawing = false;
    canvas.style.cursor = "default";
    console.log("绘制完成!");
    // 如果需要保留所有绘制的平行四边形,可以在这里把当前图形的顶点数据存入数组,每次重绘遍历数组绘制
  }

  // 处理鼠标意外移出Canvas的情况
  canvas.onmouseleave = function() {
    if (isDrawing) {
      isDrawing = false;
      canvas.style.cursor = "default";
    }
  }
}
</script>

这个方案直接用Canvas原生API绘图,你可以灵活调整倾斜系数来改变平行四边形的形状,还能轻松实现“保留所有绘制图形”的功能——只需要把每个完成的平行四边形的顶点数据保存到数组里,每次重绘时遍历数组绘制即可。


两种方案各有优劣:方案一可以复用你现有的div结构,改动较小;方案二更贴近Canvas的原生用法,功能扩展性更强,我个人更推荐第二种。

内容的提问来源于stack exchange,提问作者popeye

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:49:49