You need to enable JavaScript to run this app. 如何用JS实现Canvas上支持拖拽顶点调整角度的平行四边形绘制
优惠活动
大模型
产品
解决方案
定价
更多

如何用JS实现Canvas上支持拖拽顶点调整角度的平行四边形绘制

平行四边形拖拽绘制功能异常修复方案

核心问题定位

  • 第一版参考修改代码的错误点:drawPolygon函数每次触发渲染时都会调用GetMassCenterAndFourthPoint往顶点数组新增第四个点,多次调用后数组长度持续增长,导致顶点计算、渲染逻辑完全错乱
  • 第二版自研代码的错误点:缺少拖拽所需的鼠标按下、移动、抬起全流程事件处理,也没有实现鼠标位置和顶点的碰撞检测逻辑,无法触发拖拽行为
  • 平行四边形第四顶点不需要持久化存储,只要保证前三个顶点是用户可编辑的,每次渲染前基于「对边平行且相等」的规则动态计算即可,你原本的计算逻辑point4 = p0 + p2 - p1本身是正确的

修复后完整可运行代码

<canvas id="canvas" width="600" height="600"></canvas>
canvas {
  border: 1px solid black;
}
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
const cw = canvas.width;
const ch = canvas.height;
let mouse = {};
let draggableIndex = -1;

context.lineWidth = 2;
context.strokeStyle = "blue";

// 只存储用户可编辑的前三个顶点
const coordinates = [];
let isDrawFinished = false;

canvas.addEventListener("mousedown", handleMouseDown);
canvas.addEventListener("mousemove", handleMouseMove);
canvas.addEventListener("mouseup", handleMouseUp);

// 动态计算第四顶点
function getFourthPoint(points) {
  return {
    x: points[2].x - points[1].x + points[0].x,
    y: points[2].y - points[1].y + points[0].y
  };
}

function handleMouseDown(e) {
  mouse = getMousePos(canvas, e);
  if (isDrawFinished) {
    // 检测鼠标点击在哪个顶点上
    for (let i = 0; i < coordinates.length; i++) {
      context.beginPath();
      context.arc(coordinates[i].x, coordinates[i].y, 5, 0, 2 * Math.PI);
      if (context.isPointInPath(mouse.x, mouse.y)) {
        draggableIndex = i;
        break;
      }
    }
  } else {
    // 绘制阶段收集前三个顶点
    coordinates.push({x: mouse.x, y: mouse.y});
    render();
    if (coordinates.length === 3) {
      isDrawFinished = true;
    }
  }
}

function handleMouseMove(e) {
  if (isDrawFinished && draggableIndex !== -1) {
    mouse = getMousePos(canvas, e);
    // 更新被拖拽的顶点坐标
    coordinates[draggableIndex].x = mouse.x;
    coordinates[draggableIndex].y = mouse.y;
    render();
  }
}

function handleMouseUp() {
  draggableIndex = -1;
}

function render() {
  context.clearRect(0, 0, cw, ch);
  // 绘制平行四边形
  context.beginPath();
  context.moveTo(coordinates[0].x, coordinates[0].y);
  context.lineTo(coordinates[1].x, coordinates[1].y);
  context.lineTo(coordinates[2].x, coordinates[2].y);
  if (isDrawFinished) {
    const p4 = getFourthPoint(coordinates);
    context.lineTo(p4.x, p4.y);
  }
  context.closePath();
  context.stroke();

  // 绘制所有可拖拽顶点的标识圆圈
  coordinates.forEach(point => {
    context.beginPath();
    context.arc(point.x, point.y, 5, 0, 2 * Math.PI);
    context.stroke();
  });
  // 如果绘制完成,也给第四顶点画标识(不需要可以直接删掉这段)
  if (isDrawFinished) {
    const p4 = getFourthPoint(coordinates);
    context.beginPath();
    context.arc(p4.x, p4.y, 5, 0, 2 * Math.PI);
    context.stroke();
  }
}

// 获取鼠标在canvas内的相对坐标
function getMousePos(canvas, evt) {
  const clientRect = canvas.getBoundingClientRect();
  return {
    x: Math.round(evt.clientX - clientRect.left),
    y: Math.round(evt.clientY - clientRect.top)
  };
}

功能说明

  1. 点击三次画布确定平行四边形的前三个顶点,自动生成第四个顶点完成绘制
  2. 可以拖拽前三个顶点任意调整位置,第四个顶点会自动同步更新,始终保持平行四边形结构
  3. 所有顶点都有可见的圆圈标识,方便点击拖拽

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 22:54:02