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

在Konva.js中锚点不变时调整线段尺寸、旋转并追踪宽高与旋转角度

在Konva.js中固定锚点调整线段尺寸、旋转并追踪参数

核心思路

所有变换(缩放、旋转)都围绕固定锚点计算,通过更新线段的端点坐标实现效果,同时实时计算并追踪所需参数。

一、固定锚点调整尺寸

实现逻辑

固定线段的一个端点作为锚点,拖拽另一个端点或按比例缩放,重新计算非锚点端点的坐标,保持锚点位置不变。

代码示例

// 初始化画布、图层
const stage = new Konva.Stage({
  container: 'container',
  width: 800,
  height: 600
});
const layer = new Konva.Layer();
stage.add(layer);

// 定义固定锚点与初始线段端点
const anchor = { x: 100, y: 100 };
let endPoint = { x: 300, y: 200 };

// 创建线段
const line = new Konva.Line({
  points: [anchor.x, anchor.y, endPoint.x, endPoint.y],
  stroke: '#000',
  strokeWidth: 2
});
layer.add(line);

// 创建尺寸调整手柄
const resizeHandle = new Konva.Circle({
  x: endPoint.x,
  y: endPoint.y,
  radius: 8,
  fill: 'red',
  draggable: true
});
layer.add(resizeHandle);

// 拖拽调整尺寸逻辑
resizeHandle.on('dragmove', () => {
  const pos = resizeHandle.position();
  // 更新线段端点,锚点固定不动
  line.points([anchor.x, anchor.y, pos.x, pos.y]);
  endPoint = pos;
  
  // 实时追踪参数
  const length = Math.sqrt(Math.pow(pos.x - anchor.x, 2) + Math.pow(pos.y - anchor.y, 2));
  const bbox = line.getClientRect();
  console.log('线段长度:', length);
  console.log('包围盒宽:', bbox.width, '高:', bbox.height);
  
  layer.batchDraw();
});

二、固定锚点旋转线段

实现逻辑

围绕锚点旋转时,利用三角函数计算非锚点端点旋转后的坐标,公式如下(θ为旋转弧度):

x' = (x1 - x0)×cosθ - (y1 - y0)×sinθ + x0
y' = (x1 - x0)×sinθ + (y1 - y0)×cosθ + x0
其中(x0,y0)是锚点,(x1,y1)是原非锚点端点。

代码示例

// 创建旋转手柄
const rotateHandle = new Konva.Circle({
  x: (anchor.x + endPoint.x)/2 + 50,
  y: (anchor.y + endPoint.y)/2 + 50,
  radius: 8,
  fill: 'blue',
  draggable: true
});
layer.add(rotateHandle);

let startAngle = 0;
// 拖拽开始时记录初始角度
rotateHandle.on('dragstart', () => {
  const dx = endPoint.x - anchor.x;
  const dy = endPoint.y - anchor.y;
  startAngle = Math.atan2(dy, dx) * 180 / Math.PI; // 转换为角度
});

// 拖拽旋转逻辑
rotateHandle.on('dragmove', () => {
  const handlePos = rotateHandle.position();
  // 计算当前手柄相对锚点的角度
  const dx = handlePos.x - anchor.x;
  const dy = handlePos.y - anchor.y;
  const currentAngle = Math.atan2(dy, dx) * 180 / Math.PI;
  const rotateDiff = currentAngle - startAngle;
  
  // 计算旋转后的端点坐标
  const rad = rotateDiff * Math.PI / 180;
  const oldDx = endPoint.x - anchor.x;
  const oldDy = endPoint.y - anchor.y;
  const newDx = oldDx * Math.cos(rad) - oldDy * Math.sin(rad);
  const newDy = oldDx * Math.sin(rad) + oldDy * Math.cos(rad);
  const newEnd = { x: anchor.x + newDx, y: anchor.y + newDy };
  
  // 更新线段、手柄位置
  line.points([anchor.x, anchor.y, newEnd.x, newEnd.y]);
  resizeHandle.position(newEnd);
  // 保持旋转手柄在线段垂直外侧,提升操作直观性
  const midX = (anchor.x + newEnd.x)/2;
  const midY = (anchor.y + newEnd.y)/2;
  const lineLen = Math.sqrt(newDx**2 + newDy**2);
  const perpDx = -newDy / lineLen * 50;
  const perpDy = newDx / lineLen * 50;
  rotateHandle.position({ x: midX + perpDx, y: midY + perpDy });
  
  // 实时追踪旋转角度
  console.log('当前旋转角度:', rotateDiff);
  
  endPoint = newEnd;
  startAngle = currentAngle;
  layer.batchDraw();
});

三、参数追踪方法

  • 线段长度:通过勾股定理计算两个端点的直线距离。
  • 包围盒宽高:调用line.getClientRect()获取线段的 bounding box,该对象包含width和height属性。
  • 旋转角度:通过Math.atan2(dy, dx) * 180 / Math.PI计算线段当前与x轴的夹角,或记录旋转操作的累计角度差。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 14:13:12