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

如何修复可拖拽缩放(DRR)元素的宽高比,实现等比例调整

实现方案

你只需要修改JS部分代码即可实现1:1等比例锁定,同时保留原有拖拽、缩放的定位逻辑无卡顿偏移,CSS和HTML无需改动。

核心修改逻辑

  1. 新增全局固定宽高比配置,可按需调整比例
  2. 在缩放的鼠标移动回调中,同步宽高数值的同时,修正对应的偏移参数,保证位置计算准确

完整修改后的JS代码

var box = document.getElementById("box");
var boxWrapper = document.getElementById("box-wrapper");
const minWidth = 40;
const minHeight = 40;
// 新增:固定宽高比,1表示1:1正方形,可按需修改为其他值
const ASPECT_RATIO = 1;

var initX, initY, mousePressX, mousePressY, initW, initH, initRotate;

function repositionElement(x, y) {
    boxWrapper.style.left = x + 'px';
    boxWrapper.style.top = y + 'px';
}

function resize(w, h) {
    box.style.width = w + 'px';
    box.style.height = h + 'px';
}


function getCurrentRotation(el) {
    var st = window.getComputedStyle(el, null);
    var tm = st.getPropertyValue("-webkit-transform") ||
        st.getPropertyValue("-moz-transform") ||
        st.getPropertyValue("-ms-transform") ||
        st.getPropertyValue("-o-transform") ||
        st.getPropertyValue("transform") || "none";
    if (tm != "none") {
        var values = tm.split('(')[1].split(')')[0].split(',');
        var angle = Math.round(Math.atan2(values[1], values[0]) * (180 / Math.PI));
        return (angle < 0 ? angle + 360 : angle);
    }
    return 0;
}

function rotateBox(deg) {
    boxWrapper.style.transform = `rotate(${deg}deg)`;
}

// drag support
boxWrapper.addEventListener('mousedown', function (event) {
    if (event.target.className.indexOf("dot") > -1) {
        return;
    }

    initX = this.offsetLeft;
    initY = this.offsetTop;
    mousePressX = event.clientX;
    mousePressY = event.clientY;


    function eventMoveHandler(event) {
        repositionElement(initX + (event.clientX - mousePressX),
            initY + (event.clientY - mousePressY));
    }

    boxWrapper.addEventListener('mousemove', eventMoveHandler, false);
    window.addEventListener('mouseup', function eventEndHandler() {
        boxWrapper.removeEventListener('mousemove', eventMoveHandler, false);
        window.removeEventListener('mouseup', eventEndHandler);
    }, false);

}, false);
// done drag support

// handle resize
var rightMid = document.getElementById("right-mid");
var leftMid = document.getElementById("left-mid");
var topMid = document.getElementById("top-mid");
var bottomMid = document.getElementById("bottom-mid");

var leftTop = document.getElementById("left-top");
var rightTop = document.getElementById("right-top");
var rightBottom = document.getElementById("right-bottom");
var leftBottom = document.getElementById("left-bottom");

function resizeHandler(event, left = false, top = false, xResize = false, yResize = false) {
    initX = boxWrapper.offsetLeft;
    initY = boxWrapper.offsetTop;
    mousePressX = event.clientX;
    mousePressY = event.clientY;

    initW = box.offsetWidth;
    initH = box.offsetHeight;

    initRotate = getCurrentRotation(boxWrapper);
    var initRadians = initRotate * Math.PI / 180;
    var cosFraction = Math.cos(initRadians);
    var sinFraction = Math.sin(initRadians);
    function eventMoveHandler(event) {
        var wDiff = (event.clientX - mousePressX);
        var hDiff = (event.clientY - mousePressY);
        var rotatedWDiff = cosFraction * wDiff + sinFraction * hDiff;
        var rotatedHDiff = cosFraction * hDiff - sinFraction * wDiff;

        var newW = initW, newH = initH, newX = initX, newY = initY;

        if (xResize) {
            if (left) {
                newW = initW - rotatedWDiff;
                if (newW < minWidth) {
                  newW = minWidth;
                  rotatedWDiff = initW - minWidth;
                }
            } else {
                newW = initW + rotatedWDiff;
                if (newW < minWidth) {
                  newW = minWidth;
                  rotatedWDiff = minWidth - initW;
                }
            }
        }

        if (yResize) {
            if (top) {
                newH = initH - rotatedHDiff;
                if (newH < minHeight) {
                  newH = minHeight;
                  rotatedHDiff = initH - minHeight;
                }
            } else {
                newH = initH + rotatedHDiff;
                if (newH < minHeight) {
                  newH = minHeight;
                  rotatedHDiff = minHeight - initH;
                }
            }
        }

        // 新增:同步宽高保持固定比例
        if (xResize && yResize) {
            // 拖拽角的情况,取变化幅度更大的维度作为基准
            const wChange = Math.abs(newW - initW);
            const hChange = Math.abs(newH - initH);
            if (wChange > hChange) {
                newH = newW * ASPECT_RATIO;
            } else {
                newW = newH / ASPECT_RATIO;
            }
        } else if (xResize) {
            // 拖拽左右边的情况,以宽度为基准同步高度
            newH = newW * ASPECT_RATIO;
        } else if (yResize) {
            // 拖拽上下边的情况,以高度为基准同步宽度
            newW = newH / ASPECT_RATIO;
        }
        // 修正最小尺寸限制
        if (newW < minWidth) {
            newW = minWidth;
            newH = newW * ASPECT_RATIO;
        }
        if (newH < minHeight) {
            newH = minHeight;
            newW = newH / ASPECT_RATIO;
        }
        // 同步更新偏移量,保证位置计算准确
        rotatedWDiff = left ? initW - newW : newW - initW;
        rotatedHDiff = top ? initH - newH : newH - initH;

        // 重新计算位置
        newX = initX + 0.5 * rotatedWDiff * cosFraction - 0.5 * rotatedHDiff * sinFraction;
        newY = initY + 0.5 * rotatedWDiff * sinFraction + 0.5 * rotatedHDiff * cosFraction;

        resize(newW, newH);
        repositionElement(newX, newY);
    }


    window.addEventListener('mousemove', eventMoveHandler, false);
    window.addEventListener('mouseup', function eventEndHandler() {
        window.removeEventListener('mousemove', eventMoveHandler, false);
        window.removeEventListener('mouseup', eventEndHandler);
    }, false);
}


rightMid.addEventListener('mousedown', e => resizeHandler(e, false, false, true, false));
leftMid.addEventListener('mousedown', e => resizeHandler(e, true, false, true, false));
topMid.addEventListener('mousedown', e => resizeHandler(e, false, true, false, true));
bottomMid.addEventListener('mousedown', e => resizeHandler(e, false, false, false, true));
leftTop.addEventListener('mousedown', e => resizeHandler(e, true, true, true, true));
rightTop.addEventListener('mousedown', e => resizeHandler(e, false, true, true, true));
rightBottom.addEventListener('mousedown', e => resizeHandler(e, false, false, true, true));
leftBottom.addEventListener('mousedown', e => resizeHandler(e, true, false, true, true));

// handle rotation
var rotate = document.getElementById("rotate");
rotate.addEventListener('mousedown', function (event) {
    initX = this.offsetLeft;
    initY = this.offsetTop;
    mousePressX = event.clientX;
    mousePressY = event.clientY;


    var arrow = document.querySelector("#box");
    var arrowRects = arrow.getBoundingClientRect();
    var arrowX = arrowRects.left + arrowRects.width / 2;
    var arrowY = arrowRects.top + arrowRects.height / 2;

    function eventMoveHandler(event) {
        var angle = Math.atan2(event.clientY - arrowY, event.clientX - arrowX) + Math.PI / 2;
        rotateBox(angle * 180 / Math.PI);
    }

    window.addEventListener('mousemove', eventMoveHandler, false);

    window.addEventListener('mouseup', function eventEndHandler() {
        window.removeEventListener('mousemove', eventMoveHandler, false);
        window.removeEventListener('mouseup', eventEndHandler);
    }, false);
}, false);

resize(200, 200);
repositionElement(200, 200);

使用说明

如果需要切换其他宽高比,直接修改顶部ASPECT_RATIO的数值即可,比如设置为16/9就会固定为16:9的宽高比。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 02:27:03