Konva.js中多边形绘制、网格吸附及尺寸修改实现求助
Konva.js 多边形绘制(网格吸附+可编辑尺寸)实现方案
需求说明
需要在Konva.js中实现:
- 多边形绘制功能
- 绘制时支持网格吸附
- 绘制完成后可拖拽顶点修改多边形尺寸
以下是基于提供的代码改造后的完整实现方案:
完整实现代码
<!DOCTYPE html> <html> <head> <script src="https://unpkg.com/konva@8.4.3/konva.min.js"></script> <meta charset="utf-8" /> <title>Konva 多边形绘制(网格吸附+可编辑)</title> <style> body { padding: 0; margin: 0; overflow: hidden; } #container1 { display: inline-block; width: 600px; height: 300px; background-color: silver; overflow: hidden; position: relative; background-image: linear-gradient(rgba(0,0,0,.1) 1px, transparent 1px), linear-gradient(90deg, rgba(0,0,0,.1) 1px, transparent 1px); background-size: 20px 20px; /* 网格大小 */ } #controls { position: absolute; top: 4px; left: 4px; } </style> </head> <body> <div id='container1'></div> <div id="controls"> <button id="export">导出坐标</button> <button id="reset">重置画布</button> <textarea id="points" style="display:none; width:200px; height:100px;"></textarea> </div> <script> // 网格大小配置 const GRID_SIZE = 20; // 坐标吸附到网格 function snapToGrid(x, y) { return { x: Math.round(x / GRID_SIZE) * GRID_SIZE, y: Math.round(y / GRID_SIZE) * GRID_SIZE }; } // 初始化舞台 var s1 = new Konva.Stage({container: 'container1', width: 600, height: 300}); // 多边形图层 var polygonLayer = new Konva.Layer({draggable: false}); s1.add(polygonLayer); // 顶点拖拽图层 var pointLayer = new Konva.Layer({draggable: false}); s1.add(pointLayer); // 点击捕获区域 var r = new Konva.Rect({x:0, y: 0, width: 600, height: 300, fill: 'black', opacity: 0.1}) pointLayer.add(r) s1.draw() // 可拖拽顶点类 var DragPoint = function(path, opts, doPush){ var path = path; this.x = opts.x; this.y = opts.y; this.fixed = opts.fixed; this.id = randId(); if (doPush){ path.pts.push(this); } // 生成随机ID function randId() { return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(2, 10); } // 标记为固定顶点(绘制时添加的点) this.makeFixed = function(){ this.fixed = true; s1.find('#' + this.id).fill(path.fillColor); } this.kill = function(){ s1.find('#' + this.id).remove(); } this.draw = function(){ var circleId = this.id; var pt = new Konva.Circle({ id: circleId, x: this.x, y: this.y, radius: 5, opacity: path.pointOpacity, strokeWidth: 2, stroke: path.strokeColor, fill: 'transparent', draggable: true }) pt.on('dragstart', function(){ path.drawState = 'dragging'; }) pt.on('dragmove', function(){ var rawPos = this.getPosition(); var pos = snapToGrid(rawPos.x, rawPos.y); path.updatePt(this.id(), pos); path.draw(); }) pt.on('dragend', function(){ path.drawState = 'drawing'; var rawPos = this.getPosition(); var pos = snapToGrid(rawPos.x, rawPos.y); path.updatePt(this.getId(), pos); path.draw(); }) if (this.fixed){ this.makeFixed(); } path.ptLayer.add(pt); path.draw(); } } // 多边形绘制管理类 var PolygonPath = function() { this.polygonLayer = null; this.ptLayer = null; this.drawState = ''; this.pts = []; // 存储顶点 this.drawingPolygon = null; // 添加顶点 this.addPt = function(pos, isFixed){ if (this.drawState === 'dragging'){ return null; } // 坐标吸附到网格 const snappedPos = snapToGrid(pos.x, pos.y); var pt = new DragPoint(this, {x: snappedPos.x, y: snappedPos.y, fixed: isFixed}, true); pt.draw(); pt.makeFixed(); this.draw(); } // 更新顶点位置 this.updatePt = function(id, pos){ for (var i = 0; i < this.pts.length; i++){ if (this.pts[i].id === id){ this.pts[i].x = pos.x; this.pts[i].y = pos.y; break; } } } // 绘制多边形 this.draw = function (){ if (this.drawingPolygon){ this.drawingPolygon.remove(); } // 转换顶点格式为Konva.Polygon需要的数组 var pointsArr = []; this.pts.forEach(pt => { pointsArr.push(pt.x, pt.y); }); // 绘制闭合多边形 this.drawingPolygon = new Konva.Polygon({ points: pointsArr, stroke: this.strokeColor, strokeWidth: 2, fill: this.fillColor, fillOpacity: 0.3 }); this.polygonLayer.add(this.drawingPolygon); this.ptLayer.draw(); this.polygonLayer.draw(); } // 导出顶点坐标 this.exportPoints = function(){ var list = []; for (var i = 0; i < this.pts.length; i++){ var pt = this.pts[i]; if (pt.fixed){ list.push({x: pt.x, y: pt.y}); } } return list; } // 重置画布 this.reset = function(){ this.pts.forEach(pt => pt.kill()); this.pts = []; if (this.drawingPolygon){ this.drawingPolygon.remove(); } this.polygonLayer.draw(); this.ptLayer.draw(); } } var polygonPath = new PolygonPath(); polygonPath.polygonLayer = polygonLayer; polygonPath.ptLayer = pointLayer; polygonPath.fillColor = 'AliceBlue'; polygonPath.strokeColor = 'blue'; polygonPath.pointOpacity = 1; // 点击画布添加顶点 s1.on('mouseup touchend', function () { polygonPath.addPt(s1.getPointerPosition(), true); }); // 导出按钮事件 document.getElementById('export').addEventListener('click', function(){ const btn = this; const textarea = document.getElementById('points'); if (btn.textContent === "导出坐标"){ btn.textContent = '隐藏'; textarea.style.display = 'block'; textarea.value = JSON.stringify(polygonPath.exportPoints()); } else { btn.textContent = '导出坐标'; textarea.style.display = 'none'; } }) // 重置按钮事件 document.getElementById('reset').addEventListener('click', function(){ polygonPath.reset(); }) </script> </body> </html>
关键功能说明
1. 网格吸附实现
- 定义
GRID_SIZE配置网格大小,画布背景添加网格线辅助可视化 - 实现
snapToGrid函数,将坐标按网格大小取整对齐 - 在添加顶点、拖拽顶点的全流程中,对坐标进行吸附处理,确保所有顶点都落在网格节点上
2. 多边形可编辑功能
- 使用
Konva.Polygon代替原有的Konva.Line,自动生成闭合路径 - 保留拖拽顶点逻辑,拖拽任意顶点即可修改多边形的形状和尺寸,拖拽过程中坐标自动吸附网格
- 添加重置按钮,支持清空画布重新绘制
内容的提问来源于stack exchange,提问作者Anthony Wratten
相关产品推荐
相关产品推荐

