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

实现Pane元素拖动旋转时保持当前角度,避免跳回初始位置

问题:拖动旋转按钮时元素跳回初始位置,无法基于当前角度继续旋转

我一直尝试编写JavaScript代码,实现点击并拖动旋转按钮时,让pane元素从当前角度继续旋转。但目前的问题是,拖动时元素会先跳回初始位置,之后才能进行旋转,始终无法解决这个问题,希望能得到帮助!

原代码如下:

//ROTATION 
  let rotationHistory = [];
  var $rotateBtn = $('#pane_center');
  const $rotationInput = $('#rotation');
  const $pane = $('#pane');
  const input = document.getElementById('rotation');

document.addEventListener('mouseup', function() {
  let rotation = pane.style.transform ? parseInt(pane.style.transform.match(/-?\d+/g)[0]) : 0;
  rotation = (rotation % 360 + 360) % 360;
  input.value = rotation;
});

// Update the input field with the current rotation of the pane
$rotationInput.val(parseInt($pane.css('transform').split(/[()]/)[1]) || 0);

// Update the rotation of the pane when the input value changes
document.getElementById('rotation').addEventListener('input', function() {
  const rotationInput = document.getElementById('rotation');
  const rotation = parseInt(rotationInput.value, 10);
  document.getElementById('pane').style.transform = 'rotate(' + rotation + 'deg)';
});
  
    function get_degrees(mouse_x, mouse_y) {
  const pane = $('#pane');
  const radius  = pane.outerWidth() / 2;
  const center_x    = pane.offset().left + radius;
    const center_y  = pane.offset().top + radius;

  const radians = Math.atan2(mouse_x - center_x, mouse_y - center_y);
    const degrees   = Math.round((radians * (180 / Math.PI) * -1) + 100);

    return degrees;
    }
  
$rotateBtn.on('mousedown touchstart', function (event) {
  $(document).off("mousemove touchmove", onMove);
  
  const click_degrees = get_degrees(event.pageX, event.pageY);

  $(document).bind('mousemove touchmove', click_degrees, function(event) {
    const pane = $('#pane');
    let degrees = get_degrees(event.pageX, event.pageY) - click_degrees;
    pane.css('transform', 'rotate('+degrees+'deg)');
    
  // Store the current rotation in the history array
  rotationHistory.push(degrees);
  });
});

  animate();

  function onUp(e) {       
  $(document).off("mousemove");
  $(document).off("touchend");
  $(document).on("mousemove", onMove);
  calc(e);
  clicked = null;
  }
  
  $('#undoBtn').on('click', function(event) {
  rotationHistory.pop();
  const previousRotation = rotationHistory[rotationHistory.length - 1] || 0;
  $('#pane').css('transform', 'rotate(' + previousRotation + 'deg)');
});
  
  $('#redoBtn').on('click', function(event) {
  const nextRotation = rotationHistory[rotationHistory.length] || 0;
  if (nextRotation) {
    $('#pane').css('transform', 'rotate(' + nextRotation + 'deg)');
    rotationHistory.push(nextRotation);
  }
});
解决方案

问题根源

  1. 未获取当前旋转角度:拖动开始时没有读取元素已有的旋转角度,直接基于鼠标位置计算相对偏移,导致每次拖动都从0度开始,出现跳回初始位置的问题。
  2. 角度计算错误:get_degrees函数中Math.atan2的参数顺序颠倒,还添加了无意义的+100偏移,导致角度计算完全错误。
  3. 旋转角度解析不可靠:原代码通过字符串截取解析旋转角度,无法处理浏览器自动转换的matrix格式变换,导致获取当前角度失败。

修正后的完整代码

// ROTATION 
let rotationHistory = [];
let redoStack = []; // 新增redo栈,记录被undo的操作
const $rotateBtn = $('#pane_center');
const $rotationInput = $('#rotation');
const $pane = $('#pane');
const input = document.getElementById('rotation');

// 准确获取元素当前旋转角度
function getCurrentRotation($element) {
  const transform = $element.css('transform');
  if (transform === 'none') return 0;
  // 解析matrix变换中的旋转角度
  const matrix = transform.match(/matrix\(([^)]+)\)/)[1].split(',');
  const a = parseFloat(matrix[0]);
  const b = parseFloat(matrix[1]);
  let angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));
  return (angle + 360) % 360;
}

// 计算鼠标位置相对于元素中心的角度
function get_degrees(mouse_x, mouse_y) {
  const radius = $pane.outerWidth() / 2;
  const center_x = $pane.offset().left + radius;
  const center_y = $pane.offset().top + radius;

  // Math.atan2参数顺序为(纵坐标差, 横坐标差)
  const radians = Math.atan2(mouse_y - center_y, mouse_x - center_x);
  let degrees = Math.round(radians * (180 / Math.PI));
  // 保持角度在0-360范围内
  return (degrees + 360) % 360;
}

// 鼠标松开时更新输入框并记录旋转历史
document.addEventListener('mouseup', function() {
  const rotation = getCurrentRotation($pane);
  input.value = rotation;
  // 仅在拖动结束时记录历史,避免数组冗余
  rotationHistory.push(rotation);
  // 每次新操作后清空redo栈
  redoStack = [];
});

// 初始化输入框为当前旋转角度
$rotationInput.val(getCurrentRotation($pane));

// 输入框改变时更新元素旋转
document.getElementById('rotation').addEventListener('input', function() {
  const rotation = parseInt(this.value, 10) || 0;
  $pane.css('transform', `rotate(${rotation}deg)`);
});

// 拖动旋转逻辑
$rotateBtn.on('mousedown touchstart', function (event) {
  $(document).off("mousemove touchmove");
  
  // 获取当前已有的旋转角度,作为拖动的基础
  const currentRotation = getCurrentRotation($pane);
  // 获取鼠标按下时的角度
  const clickDegrees = get_degrees(event.pageX, event.pageY);

  $(document).bind('mousemove touchmove', {currentRotation, clickDegrees}, function(event) {
    const data = event.data;
    const currentMouseDegrees = get_degrees(event.pageX, event.pageY);
    // 计算总旋转角度:基础角度 + 鼠标拖动的偏移量
    let degrees = data.currentRotation + (currentMouseDegrees - data.clickDegrees);
    degrees = (degrees + 360) % 360;
    $pane.css('transform', `rotate(${degrees}deg)`);
  });
});

// 鼠标松开时解绑拖动事件
$(document).on('mouseup touchend', function() {
  $(document).off("mousemove touchmove");
});

// 撤销操作
$('#undoBtn').on('click', function() {
  if (rotationHistory.length > 0) {
    const popped = rotationHistory.pop();
    redoStack.push(popped);
    const previousRotation = rotationHistory.length > 0 ? rotationHistory[rotationHistory.length - 1] : 0;
    $pane.css('transform', `rotate(${previousRotation}deg)`);
    input.value = previousRotation;
  }
});

// 重做操作
$('#redoBtn').on('click', function() {
  if (redoStack.length > 0) {
    const nextRotation = redoStack.pop();
    rotationHistory.push(nextRotation);
    $pane.css('transform', `rotate(${nextRotation}deg)`);
    input.value = nextRotation;
  }
});

// 原代码中的animate和onUp函数(如果有实际作用保留,否则可移除)
function animate() {
  // 原动画逻辑(如果需要保留)
}

function onUp(e) {       
  $(document).off("mousemove");
  $(document).off("touchend");
  clicked = null;
}

关键修改点说明

  1. getCurrentRotation函数:通过解析CSS matrix变换准确获取当前旋转角度,兼容浏览器的自动转换。
  2. 修正get_degrees函数:调整Math.atan2的参数顺序,移除错误的+100偏移,确保角度计算准确。
  3. 拖动逻辑优化:拖动开始时获取当前旋转角度作为基础,计算拖动偏移时叠加到基础角度上,实现从当前角度继续旋转。
  4. 历史记录优化:仅在拖动结束时记录旋转状态,新增redo栈处理重做操作,避免历史记录混乱。

内容的提问来源于stack exchange,提问作者Lyder Prag Sømme

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 23:42:12