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

旋转后调整Div尺寸时固定非操作边位置的技术咨询

解决旋转后调整Div尺寸时边位置偏移的问题

当元素应用transform: rotate()后,元素的布局边界(offsetLeft/offsetTop/offsetWidth/offsetHeight)依然基于未旋转的轴对齐盒子,而鼠标拖拽的偏移是全局坐标系下的,两者不匹配导致调整尺寸时其他边位置异动。核心解决思路是将鼠标的全局偏移转换为元素旋转后的局部坐标系偏移,再进行尺寸和位置计算。

修改步骤

1. 全局存储旋转角度

确保尺寸调整逻辑能准确获取当前旋转角度,初始化角度为0:

let currentDeg = 0; // 全局存储当前旋转角度
let startDeg = 0;    // 旋转时记录初始角度,避免重置

2. 添加坐标转换函数

编写函数将全局坐标系的鼠标偏移,反向旋转后转换为元素局部坐标系的偏移:

function convertToLocalCoords(deltaX, deltaY, angleDeg) {
  const angleRad = (angleDeg * Math.PI) / 180;
  const localDeltaX = deltaX * Math.cos(angleRad) + deltaY * Math.sin(angleRad);
  const localDeltaY = -deltaX * Math.sin(angleRad) + deltaY * Math.cos(angleRad);
  return { x: localDeltaX, y: localDeltaY };
}

3. 修改尺寸调整的鼠标移动逻辑

使用转换后的局部偏移量计算尺寸变化,同时保留全局偏移更新元素位置:

document.addEventListener('mousemove', e => {
  if (!isResizing) return;
  let deltaX = e.pageX - currentX;
  let deltaY = e.pageY - currentY;

  // 转换为元素局部坐标系的偏移
  const localDelta = convertToLocalCoords(deltaX, deltaY, currentDeg);

  if (currentHandle.classList.contains('handle-n')) {
    resizableDiv.style.height = originalHeight - localDelta.y + 'px';
    resizableDiv.style.top = originalY + deltaY + 'px';
  } else if (currentHandle.classList.contains('handle-e')) {
    resizableDiv.style.width = originalWidth + localDelta.x + 'px';
  } else if (currentHandle.classList.contains('handle-s')) {
    resizableDiv.style.height = originalHeight + localDelta.y + 'px';
  } else if (currentHandle.classList.contains('handle-w')) {
    resizableDiv.style.width = originalWidth - localDelta.x + 'px';
    resizableDiv.style.left = originalX + deltaX + 'px';
  }
});

4. 优化旋转逻辑(可选)

修改旋转逻辑为围绕元素中心旋转,操作更符合直觉:

rotateHandle.addEventListener('mousedown', function(e) {
  isDragging = true;
  const rect = resizableDiv.getBoundingClientRect();
  const centerX = rect.left + rect.width / 2;
  const centerY = rect.top + rect.height / 2;
  startX = e.clientX - centerX;
  startY = e.clientY - centerY;
  startDeg = currentDeg;
});

document.addEventListener('mousemove', function(e) {
  if (!isDragging) return;
  const rect = resizableDiv.getBoundingClientRect();
  const centerX = rect.left + rect.width / 2;
  const centerY = rect.top + rect.height / 2;
  const currentMouseX = e.clientX - centerX;
  const currentMouseY = e.clientY - centerY;

  const startRad = Math.atan2(startY, startX);
  const currentRad = Math.atan2(currentMouseY, currentMouseX);
  currentDeg = startDeg + (currentRad - startRad) * (180 / Math.PI);
  resizableDiv.style.transform = `rotate(${currentDeg}deg)`;
});

完整修改后的代码

HTML(无需修改)

<div id="resizable-div">
    <div class="handle handle-n"></div>
    <div class="handle handle-e"></div>
    <div class="handle handle-s"></div>
    <div class="handle handle-w"></div>
    <div class="handle rotate-handle"></div>
</div>

CSS(无需修改)

#resizable-div {
    position: absolute;
    width: 200px;
    height: 200px;
    left: 50px;
    top: 50px;
    background-color: #ddd;
}

.handle {
    position: absolute;
    width: 10px;
    height: 10px;
    background-color: #fff;
    border: 1px solid #000;
}

.handle-n {
    top: -5px;
    left: 50%;
    transform: translateX(-50%);
    cursor: n-resize;
}

.handle-e {
    right: -5px;
    top: 50%;
    transform: translateY(-50%);
    cursor: e-resize;
}

.handle-s {
    bottom: -5px;
    left: 50%;
    transform: translateX(-50%);
    cursor: s-resize;
}

.handle-w {
    left: -5px;
    top: 50%;
    transform: translateY(-50%);
    cursor: w-resize;
}

.rotate-handle {
    bottom: 0px;
    right: 0px;
    border-radius: 50%;
    background-color: white;
    cursor: pointer;
}

JavaScript(修改后)

const resizableDiv = document.querySelector('#resizable-div');
const handles = document.querySelectorAll('.handle');

let isResizing = false;
let currentHandle = null;
let originalWidth = 0;
let originalHeight = 0;
let originalX = 0;
let originalY = 0;
let currentX = 0;
let currentY = 0;
let currentDeg = 0;
let startDeg = 0;

// 坐标转换:全局偏移转元素局部偏移
function convertToLocalCoords(deltaX, deltaY, angleDeg) {
    const angleRad = (angleDeg * Math.PI) / 180;
    const localDeltaX = deltaX * Math.cos(angleRad) + deltaY * Math.sin(angleRad);
    const localDeltaY = -deltaX * Math.sin(angleRad) + deltaY * Math.cos(angleRad);
    return { x: localDeltaX, y: localDeltaY };
}

handles.forEach(handle => {
    handle.addEventListener('mousedown', e => {
        if (handle.classList.contains('rotate-handle')) return;
        isResizing = true;
        currentHandle = handle;
        originalWidth = resizableDiv.offsetWidth;
        originalHeight = resizableDiv.offsetHeight;
        originalX = resizableDiv.offsetLeft;
        originalY = resizableDiv.offsetTop;
        currentX = e.pageX;
        currentY = e.pageY;
    });
});

document.addEventListener('mouseup', () => {
    isResizing = false;
});

document.addEventListener('mousemove', e => {
    if (!isResizing) return;
    let deltaX = e.pageX - currentX;
    let deltaY = e.pageY - currentY;

    const localDelta = convertToLocalCoords(deltaX, deltaY, currentDeg);

    if (currentHandle.classList.contains('handle-n')) {
        resizableDiv.style.height = originalHeight - localDelta.y + 'px';
        resizableDiv.style.top = originalY + deltaY + 'px';
    } else if (currentHandle.classList.contains('handle-e')) {
        resizableDiv.style.width = originalWidth + localDelta.x + 'px';
    } else if (currentHandle.classList.contains('handle-s')) {
        resizableDiv.style.height = originalHeight + localDelta.y + 'px';
    } else if (currentHandle.classList.contains('handle-w')) {
        resizableDiv.style.width = originalWidth - localDelta.x + 'px';
        resizableDiv.style.left = originalX + deltaX + 'px';
    }
});

// 旋转逻辑
const rotateHandle = document.querySelector('.rotate-handle');
let isDragging = false;
let startX = 0;
let startY = 0;

rotateHandle.addEventListener('mousedown', function(e) {
    isDragging = true;
    const rect = resizableDiv.getBoundingClientRect();
    const centerX = rect.left + rect.width / 2;
    const centerY = rect.top + rect.height / 2;
    startX = e.clientX - centerX;
    startY = e.clientY - centerY;
    startDeg = currentDeg;
});

document.addEventListener('mouseup', function() {
    isDragging = false;
});

document.addEventListener('mousemove', function(e) {
    if (!isDragging) return;
    const rect = resizableDiv.getBoundingClientRect();
    const centerX = rect.left + rect.width / 2;
    const centerY = rect.top + rect.height / 2;
    const currentMouseX = e.clientX - centerX;
    const currentMouseY = e.clientY - centerY;

    const startRad = Math.atan2(startY, startX);
    const currentRad = Math.atan2(currentMouseY, currentMouseX);
    currentDeg = startDeg + (currentRad - startRad) * (180 / Math.PI);
    resizableDiv.style.transform = `rotate(${currentDeg}deg)`;
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 03:35:42