如何实现用户悬停时Konva自定义形状显示可调整大小的控制点
Konva JS 形状调整锚点实现方案
核心实现逻辑
- 给每个自定义形状绑定
mouseenter、mouseleave事件,触发时控制调整锚点的显示、隐藏,鼠标移出形状区域且未选中形状时隐藏锚点即可 - 调整锚点用
Konva.Circle实现即可,共8个,分别对应形状包围盒的四个角、四条边中点,锚点位置始终和当前形状的getClientRect()返回的坐标做绑定 - 给每个锚点绑定
dragmove事件,拖动时根据锚点的位置动态修改形状的width、height、x、y属性,边角锚点拖动时可额外增加按住shift实现等比缩放的逻辑,符合主流绘图工具的交互习惯 - 形状大小调整完成后需要同步更新该形状关联的所有连线端点坐标,避免连线和形状脱节
核心示例代码
// 提前创建锚点专用图层,保证锚点层级高于所有形状 const anchorLayer = new Konva.Layer() stage.add(anchorLayer) // 生成调整锚点 function createAnchors(shape) { // 先清空之前的锚点 anchorLayer.find('.resize-anchor').destroy() const shapeRect = shape.getClientRect() // 8个锚点的坐标点位 const anchorPosList = [ {x: shapeRect.x, y: shapeRect.y, cursor: 'nwse-resize', name: 'top-left'}, {x: shapeRect.x + shapeRect.width/2, y: shapeRect.y, cursor: 'ns-resize', name: 'top-center'}, {x: shapeRect.x + shapeRect.width, y: shapeRect.y, cursor: 'nesw-resize', name: 'top-right'}, {x: shapeRect.x + shapeRect.width, y: shapeRect.y + shapeRect.height/2, cursor: 'ew-resize', name: 'middle-right'}, {x: shapeRect.x + shapeRect.width, y: shapeRect.y + shapeRect.height, cursor: 'nwse-resize', name: 'bottom-right'}, {x: shapeRect.x + shapeRect.width/2, y: shapeRect.y + shapeRect.height, cursor: 'ns-resize', name: 'bottom-center'}, {x: shapeRect.x, y: shapeRect.y + shapeRect.height, cursor: 'nesw-resize', name: 'bottom-left'}, {x: shapeRect.x, y: shapeRect.y + shapeRect.height/2, cursor: 'ew-resize', name: 'middle-left'} ] anchorPosList.forEach(pos => { const anchor = new Konva.Circle({ x: pos.x, y: pos.y, radius: 5, fill: '#ffffff', stroke: '#1677ff', strokeWidth: 2, draggable: true, name: 'resize-anchor', cursor: pos.cursor }) // 锚点拖动逻辑 anchor.on('dragmove', (e) => { const curAnchor = e.target const anchorName = curAnchor.name() const originX = shape.x() const originY = shape.y() const originW = shape.width() const originH = shape.height() // 以左上角锚点为例,其余锚点逻辑对应调整即可 if (anchorName === 'top-left') { const newW = originX + originW - curAnchor.x() const newH = originY + originH - curAnchor.y() // 增加最小宽高限制,避免缩放到0 if (newW > 10 && newH > 10) { shape.width(newW) shape.height(newH) shape.x(curAnchor.x()) shape.y(curAnchor.y()) } } // 拖动时实时更新锚点位置 updateAnchors(shape) // 同步更新关联的连线 updateConnectedLines(shape) stage.batchDraw() }) anchorLayer.add(anchor) }) anchorLayer.show() } // 更新锚点位置 function updateAnchors(shape) { const shapeRect = shape.getClientRect() const anchors = anchorLayer.find('.resize-anchor') const newPos = [ [shapeRect.x, shapeRect.y], [shapeRect.x + shapeRect.width/2, shapeRect.y], [shapeRect.x + shapeRect.width, shapeRect.y], [shapeRect.x + shapeRect.width, shapeRect.y + shapeRect.height/2], [shapeRect.x + shapeRect.width, shapeRect.y + shapeRect.height], [shapeRect.x + shapeRect.width/2, shapeRect.y + shapeRect.height], [shapeRect.x, shapeRect.y + shapeRect.height], [shapeRect.x, shapeRect.y + shapeRect.height/2] ] anchors.forEach((anchor, index) => { anchor.x(newPos[index][0]) anchor.y(newPos[index][1]) }) }
优化建议及相关资源
- Konva官方已经封装了
Transformer组件,内置了完整的形状调整、旋转、斜切能力,不需要自行实现锚点逻辑,可直接调用,支持自定义锚点样式、缩放比例限制、旋转开关等配置,适配所有Konva内置形状及自定义形状 - 开源绘图工具tldraw、excalidraw的源码中也有成熟的形状调整交互实现,可以参考其锚点计算、缩放边界处理、对齐辅助线相关的逻辑
内容的提问来源于stack exchange,提问作者Dijkstra Algorithm
相关产品推荐
相关产品推荐

