如何在PaperJS中为选中形状的bounding rectangle添加侧边中点手柄
如何在PaperJS中为选中形状的bounding rectangle添加中点手柄
嘿,我刚好折腾过类似的需求!PaperJS默认的选中手柄确实只有四个角,要给bounding rectangle的四边中点加上可拖拽的手柄其实不难,咱们可以通过监听选中事件、自定义手柄并处理拖拽交互来实现,下面是具体的思路和完整代码:
核心思路
其实就是三步走:
- 监听项目的
selectionChanged事件,当选中的形状变化时,自动创建/清理中点手柄 - 给每个选中的形状计算出四边中点的位置,生成自定义的可拖拽手柄(比如小圆圈)
- 处理手柄的拖拽逻辑,让拖拽中点时能调整对应边的位置,同时同步更新手柄和形状的位置
完整代码示例
// 用来存所有中点手柄的数组,方便后续清理和更新 let midHandles = []; // 监听选中项变化的事件,这是核心触发点 project.on('selectionChanged', function() { // 先把之前的旧手柄清掉,顺便移除对应的事件监听,避免内存泄漏 midHandles.forEach(handle => { const targetShape = handle.data.targetShape; if (targetShape) { targetShape.off('change', updateMidHandlePositions); } handle.remove(); }); midHandles = []; const selectedShapes = project.selectedItems; if (selectedShapes.length === 0) return; selectedShapes.forEach(shape => { const shapeBounds = shape.bounds; // 计算四个中点的位置:上、右、下、左 const midPoints = [ new Point(shapeBounds.center.x, shapeBounds.top), new Point(shapeBounds.right, shapeBounds.center.y), new Point(shapeBounds.center.x, shapeBounds.bottom), new Point(shapeBounds.left, shapeBounds.center.y) ]; midPoints.forEach((point, index) => { // 创建中点手柄,这里用蓝色小圆圈,你可以自己改样式 const handle = new Path.Circle({ center: point, radius: 4, fillColor: 'blue', // 存一下关联的形状和对应的边,拖拽时要用 data: { targetShape: shape, side: index // 0=上边, 1=右边, 2=下边, 3=左边 } }); // 让手柄能被选中和拖拽 handle.selected = true; midHandles.push(handle); }); // 监听形状的变换事件(移动、缩放等),实时更新手柄位置 shape.on('change', updateMidHandlePositions); }); }); // 创建一个工具来处理手柄的拖拽逻辑 const handleDragTool = new Tool(); let currentDraggingHandle = null; let initialShapeBounds = null; handleDragTool.on('mousedown', function(event) { // 检查是不是点到了咱们的中点手柄 const hitTestResult = project.hitTest(event.point, { fill: true }); if (hitTestResult && hitTestResult.item.data.targetShape) { currentDraggingHandle = hitTestResult.item; // 记录拖拽前的形状边界,避免拖拽时计算出错 initialShapeBounds = currentDraggingHandle.data.targetShape.bounds.clone(); } }); handleDragTool.on('mousedrag', function(event) { if (!currentDraggingHandle) return; const targetShape = currentDraggingHandle.data.targetShape; const side = currentDraggingHandle.data.side; const dragDelta = event.delta; // 根据拖拽的边调整形状的边界 switch (side) { case 0: // 拖拽上边中点,只调整上边位置 targetShape.bounds.top = initialShapeBounds.top + dragDelta.y; break; case 1: // 拖拽右边中点,只调整右边位置 targetShape.bounds.right = initialShapeBounds.right + dragDelta.x; break; case 2: // 拖拽下边中点,只调整下边位置 targetShape.bounds.bottom = initialShapeBounds.bottom + dragDelta.y; break; case 3: // 拖拽左边中点,只调整左边位置 targetShape.bounds.left = initialShapeBounds.left + dragDelta.x; break; } // 更新当前形状的所有中点手柄位置 updateMidHandlePositions(targetShape); }); handleDragTool.on('mouseup', function() { currentDraggingHandle = null; initialShapeBounds = null; }); // 单独写一个函数来更新单个形状的中点手柄位置 function updateMidHandlePositions(shape) { const shapeBounds = shape.bounds; const shapeHandles = midHandles.filter(handle => handle.data.targetShape === shape); if (shapeHandles.length !== 4) return; // 分别更新四个手柄的位置 shapeHandles[0].position = new Point(shapeBounds.center.x, shapeBounds.top); shapeHandles[1].position = new Point(shapeBounds.right, shapeBounds.center.y); shapeHandles[2].position = new Point(shapeBounds.center.x, shapeBounds.bottom); shapeHandles[3].position = new Point(shapeBounds.left, shapeBounds.center.y); }
一些细节说明
- 样式自定义:你可以修改手柄的
radius、fillColor甚至换成其他形状(比如正方形),来匹配你的UI风格 - 交互逻辑:拖拽中点时只会调整对应边的位置,对边保持不动,这和常见的图形编辑器逻辑一致,用户上手会很自然
- 事件清理:每次选中变化时都会清理旧的手柄和事件监听,不会留下内存垃圾
- 实时同步:监听形状的
change事件,不管是拖拽手柄还是直接移动/缩放形状,手柄位置都会跟着同步更新
这样就能实现你想要的带中点手柄的bounding rectangle效果啦!
内容的提问来源于stack exchange,提问作者matlocat
相关产品推荐
相关产品推荐

