在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
相关产品推荐
相关产品推荐

