You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在图片上拖拽已绘制的多边形并更新坐标

实现多边形拖拽与坐标更新方案

我来帮你搞定多边形的拖拽功能!咱们需要添加几个状态变量、事件监听,以及辅助逻辑来实现选中多边形拖拽时实时更新位置,并把新坐标保存回原数组的功能。下面是完整的实现思路和修改后的代码:


核心思路

要实现拖拽,咱们需要:

  • 跟踪当前选中的多边形拖拽状态
  • 在鼠标按下时判断是否点击到已有多边形
  • 拖拽过程中计算偏移量,更新多边形所有点的坐标
  • 高亮选中的多边形,提升用户体验
  • 确保拖拽结束后坐标自动保存到polygons数组(因为数组是引用类型,直接修改点的坐标就会自动保存)

修改后的完整代码

<canvas id="canvas" width="500" height="500"></canvas>
<button type="button" onclick="undoLastPoint()">Undo</button>
<img id="justanimage" />

<script>
//radius of click around the first point to close the draw
var END_CLICK_RADIUS = 15;
//the max number of points of your polygon
var MAX_POINTS = 4;
var mouseX = 0;
var mouseY = 0;
var isStarted = false;
var polygons = [];
var canvas = null;
var ctx;
var image;

// 新增拖拽相关状态变量
var selectedPolygonIndex = -1; // 当前选中的多边形索引,-1表示未选中
var isDragging = false; // 是否正在拖拽
var dragOffsetX = 0; // 鼠标相对于多边形起始点的X偏移
var dragOffsetY = 0; // 鼠标相对于多边形起始点的Y偏移

window.onload = function() {
    var background = document.getElementById('justanimage');
    //initializing canvas and draw color
    canvas = document.getElementById("canvas");
    ctx = canvas.getContext("2d");
    image = new Image();
    image.onload = function() {
        ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
    };
    image.src = 'https://images.freeimages.com/images/large-previews/2fe/butterfly-1390152.jpg';

    canvas.addEventListener("click", function(e) {
        var x = e.clientX - canvas.offsetLeft;
        var y = e.clientY - canvas.offsetTop;

        // 先判断是否点击到已有多边形(优先处理选中,再处理绘制)
        var clickedPolygonIndex = getClickedPolygonIndex(x, y);
        if (clickedPolygonIndex !== -1) {
            // 切换选中状态:点击已选中的则取消选中,否则选中新的
            selectedPolygonIndex = selectedPolygonIndex === clickedPolygonIndex ? -1 : clickedPolygonIndex;
            isStarted = false; // 选中时停止绘制
            return;
        }

        // 原来的绘制逻辑
        if (isStarted) {
            //drawing the next line, and closing the polygon if needed
            if (Math.abs(x - polygons[polygons.length - 1][0].x) < END_CLICK_RADIUS && Math.abs(y - polygons[polygons.length - 1][0].y) < END_CLICK_RADIUS) {
                isStarted = false;
            } else {
                polygons[polygons.length - 1].push(new Point(x, y));
                if (polygons[polygons.length - 1].length >= MAX_POINTS) {
                    isStarted = false;
                }
            }
        } else {
            //opening the polygon
            polygons.push([new Point(x, y)]);
            isStarted = true;
            selectedPolygonIndex = -1; // 开始绘制时取消选中
        }
    }, false);

    // 新增鼠标按下事件:触发拖拽开始
    canvas.addEventListener("mousedown", function(e) {
        if (selectedPolygonIndex === -1) return; // 未选中则不处理
        var x = e.clientX - canvas.offsetLeft;
        var y = e.clientY - canvas.offsetTop;
        // 计算鼠标相对于选中多边形第一个点的偏移量
        var selectedPolygon = polygons[selectedPolygonIndex];
        dragOffsetX = x - selectedPolygon[0].x;
        dragOffsetY = y - selectedPolygon[0].y;
        isDragging = true;
    });

    // 新增鼠标移动事件:拖拽时更新多边形坐标
    canvas.addEventListener("mousemove", function(e) {
        mouseX = e.clientX - canvas.offsetLeft;
        mouseY = e.clientY - canvas.offsetTop;

        if (isDragging && selectedPolygonIndex !== -1) {
            var selectedPolygon = polygons[selectedPolygonIndex];
            // 计算新的起始点坐标
            var newStartX = mouseX - dragOffsetX;
            var newStartY = mouseY - dragOffsetY;
            // 计算偏移量(相对于原来的起始点)
            var deltaX = newStartX - selectedPolygon[0].x;
            var deltaY = newStartY - selectedPolygon[0].y;
            // 更新多边形所有点的坐标
            selectedPolygon.forEach(function(point) {
                point.x += deltaX;
                point.y += deltaY;
            });
        }
    }, false);

    // 新增鼠标松开事件:结束拖拽
    canvas.addEventListener("mouseup", function() {
        isDragging = false;
    });

    //refresh time
    setInterval("draw();", 5);
}

//object representing a point
function Point(x, y) {
    this.x = x;
    this.y = y;
}

//resets the application
function reset() {
    isStarted = false;
    polygons = []; // 原来的points变量没用,改为清空polygons
    selectedPolygonIndex = -1; // 重置选中状态
    document.getElementById("coordinates").innerHTML = " ";
}

//draws the current shape
function draw() {
    ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
    ctx.lineWidth = 2;
    
    polygons.forEach(function(points, i) {
        ctx.beginPath();
        points.forEach(function(p, j) {
            if (j) {
                ctx.lineTo(p.x, p.y);
            } else {
                ctx.moveTo(p.x, p.y);
            }
        });
        // 闭合多边形
        ctx.lineTo(points[0].x, points[0].y);
        
        // 高亮选中的多边形
        if (i === selectedPolygonIndex) {
            ctx.strokeStyle = '#00ff00'; // 选中时用绿色
            ctx.fillStyle = 'rgba(0,255,0,0.2)'; // 半透明填充,方便识别
            ctx.fill();
        } else {
            ctx.strokeStyle = '#ff0000'; // 未选中用红色
        }
        ctx.stroke();
    });

    // 绘制正在绘制的临时线段
    if (isStarted && polygons.length > 0) {
        var lastPolygon = polygons[polygons.length - 1];
        ctx.beginPath();
        ctx.moveTo(lastPolygon[lastPolygon.length - 1].x, lastPolygon[lastPolygon.length - 1].y);
        ctx.lineTo(mouseX, mouseY);
        ctx.strokeStyle = '#ff0000';
        ctx.stroke();
    }
}

function undoLastPoint() {
    // 移除最后绘制的多边形
    if (polygons.length > 0) {
        polygons.pop();
        selectedPolygonIndex = -1; // 撤销后重置选中状态
    }
}

// 辅助函数:判断鼠标点击的位置是否在某个多边形内部
function getClickedPolygonIndex(x, y) {
    for (var i = polygons.length - 1; i >= 0; i--) {
        var points = polygons[i];
        ctx.beginPath();
        points.forEach(function(p, j) {
            if (j) {
                ctx.lineTo(p.x, p.y);
            } else {
                ctx.moveTo(p.x, p.y);
            }
        });
        ctx.lineTo(points[0].x, points[0].y);
        // 使用Canvas的isPointInPath判断点是否在多边形内部
        if (ctx.isPointInPath(x, y)) {
            return i;
        }
    }
    return -1;
}
</script>

关键修改点说明

  1. 新增状态变量

    • selectedPolygonIndex:记录当前选中的多边形索引,-1表示无选中
    • isDragging:标记是否处于拖拽状态
    • dragOffsetX/Y:记录鼠标相对于多边形起始点的偏移,避免拖拽时多边形跳转到鼠标位置
  2. 点击事件优化

    • 优先处理多边形选中逻辑:调用getClickedPolygonIndex判断点击位置是否在某个多边形内部,实现选中/取消选中切换
    • 绘制新多边形时自动取消已有选中状态
  3. 拖拽事件处理

    • mousedown:当选中多边形时,计算鼠标与多边形起始点的偏移量,开启拖拽状态
    • mousemove:拖拽时计算偏移量,更新多边形所有点的坐标(直接修改polygons数组里的Point对象,因为是引用类型,自动保存)
    • mouseup:结束拖拽状态
  4. 绘制函数优化

    • 选中的多边形用绿色高亮并添加半透明填充,方便用户识别
    • 拆分正在绘制的临时线段逻辑,让绘制和选中的视觉效果更清晰
  5. 辅助函数getClickedPolygonIndex

    • 使用Canvas原生的isPointInPathAPI判断点是否在多边形内部,精准识别选中操作

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

火山引擎 最新活动