如何在保持画布对象对边固定的同时调整其大小?
如何在保持画布对象对边固定的同时调整其大小?
嘿,我看你在做一个简易画布编辑器,现在遇到了缩放对象的问题——当前是从中心缩放,而你想要的是拖拽哪个边缘/角,就从那个边缘往外拉(对应的对边固定)对吧?
先看看你当前的效果和期望效果:
当前缩放效果

期望缩放效果

问题出在你的 resize 逻辑里,现在你是基于对象中心来计算尺寸变化,没有考虑拖拽手柄对应的对边需要固定。咱们得调整一下,在缩放时根据选中的手柄,同时修改对象的尺寸和位置,让对边保持原位。
下面是修改后的关键代码部分,我会标出来核心改动点:
首先,在 onHandleMouseDown 函数里,确保记录对象的初始位置(原代码已有,但要注意变量作用域):
function onHandleMouseDown(event, type) { event.preventDefault(); event.stopPropagation(); if (!selectedObject) return; currentHandle = event.target.id; const pos = getCanvasMousePosition(event); if (currentHandle.startsWith('rotate-')) { isRotating = true; const center = { x: selectedObject.x + selectedObject.width / 2, y: selectedObject.y + selectedObject.height / 2 }; initialAngle = Math.atan2(pos.y - center.y, pos.x - center.x) * 180 / Math.PI - selectedObject.angle; } else { isResizing = true; initialMouseX = pos.x; initialMouseY = pos.y; initialWidth = selectedObject.width; initialHeight = selectedObject.height; // 记录对象初始的x、y位置,用于后续计算 window.initialX = selectedObject.x; window.initialY = selectedObject.y; } }
然后替换核心的 resize 逻辑(修改 window.addEventListener('mousemove') 里的缩放部分):
window.addEventListener('mousemove', (event) => { if (!selectedObject) return; const pos = getCanvasMousePosition(event); const center = { x: selectedObject.x + selectedObject.width / 2, y: selectedObject.y + selectedObject.height / 2 }; if (isResizing) { // 1. 将鼠标坐标转换到对象的局部坐标系(消除旋转影响) const initialLocal = unrotatePoint(initialMouseX, initialMouseY, center.x, center.y, selectedObject.angle); const currentLocal = unrotatePoint(pos.x, pos.y, center.x, center.y, selectedObject.angle); // 2. 计算局部坐标系下的鼠标位移 const deltaX = currentLocal.x - initialLocal.x; const deltaY = currentLocal.y - initialLocal.y; let newWidth = initialWidth; let newHeight = initialHeight; let newX = window.initialX; let newY = window.initialY; // 3. 根据不同手柄,固定对应的对边,计算新尺寸和位置 switch(currentHandle) { case 'top-left-handle': // 右下角固定,左上角拖拽:宽高减少位移量,同时移动对象位置 newWidth = initialWidth - deltaX; newHeight = initialHeight - deltaY; newX = window.initialX + deltaX; newY = window.initialY + deltaY; break; case 'top-right-handle': // 左下角固定,右上角拖拽:宽度增加、高度减少,y位置上移 newWidth = initialWidth + deltaX; newHeight = initialHeight - deltaY; newY = window.initialY + deltaY; break; case 'bottom-left-handle': // 右上角固定,左下角拖拽:宽度减少、高度增加,x位置右移 newWidth = initialWidth - deltaX; newHeight = initialHeight + deltaY; newX = window.initialX + deltaX; break; case 'bottom-right-handle': // 左上角固定,右下角拖拽:直接增加宽高 newWidth = initialWidth + deltaX; newHeight = initialHeight + deltaY; break; case 'middle-left-handle': // 右边固定,左边拖拽:宽度减少,x位置右移 newWidth = initialWidth - deltaX; newX = window.initialX + deltaX; break; case 'middle-right-handle': // 左边固定,右边拖拽:宽度增加 newWidth = initialWidth + deltaX; break; case 'middle-top-handle': // 底部固定,顶部拖拽:高度减少,y位置下移 newHeight = initialHeight - deltaY; newY = window.initialY + deltaY; break; case 'middle-bottom-handle': // 顶部固定,底部拖拽:高度增加 newHeight = initialHeight + deltaY; break; } // 确保尺寸不会太小 newWidth = Math.max(10, newWidth); newHeight = Math.max(10, newHeight); // 更新对象属性 selectedObject.width = newWidth; selectedObject.height = newHeight; selectedObject.x = newX; selectedObject.y = newY; // 更新手柄和重绘 updateHandles(); drawObjects(); } // 旋转逻辑保持不变 if (isRotating) { const newAngle = Math.atan2(pos.y - center.y, pos.x - center.x) * 180 / Math.PI - initialAngle; selectedObject.angle = newAngle; updateHandles(); drawObjects(); } });
简单说下原理:
- 先把鼠标坐标转换到对象自己的局部坐标系(消除旋转影响,这样不管对象转了多少度,都能按对象自身的上下左右判断拖拽方向)
- 根据选中的手柄确定固定边,比如拖拽左上角时,右下角保持不动,所以要同时调整对象的宽高和位置
- 限制最小尺寸,避免缩成负数或过小的情况
这样修改后,你拖拽哪个边缘/角,对象就会从那个边缘向外缩放,对应的对边固定不动,和你期望的效果一致啦~
备注:内容来源于stack exchange,提问作者dxUser
相关产品推荐
相关产品推荐

