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

如何在保持画布对象对边固定的同时调整其大小?

如何在保持画布对象对边固定的同时调整其大小?

嘿,我看你在做一个简易画布编辑器,现在遇到了缩放对象的问题——当前是从中心缩放,而你想要的是拖拽哪个边缘/角,就从那个边缘往外拉(对应的对边固定)对吧?

先看看你当前的效果和期望效果:

当前缩放效果

当前调整效果

期望缩放效果

期望调整效果

问题出在你的 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 03:35:27