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

如何实现用户悬停时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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 15:54:01