实现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); } });
解决方案
问题根源
- 未获取当前旋转角度:拖动开始时没有读取元素已有的旋转角度,直接基于鼠标位置计算相对偏移,导致每次拖动都从0度开始,出现跳回初始位置的问题。
- 角度计算错误:
get_degrees函数中Math.atan2的参数顺序颠倒,还添加了无意义的+100偏移,导致角度计算完全错误。 - 旋转角度解析不可靠:原代码通过字符串截取解析旋转角度,无法处理浏览器自动转换的
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; }
关键修改点说明
getCurrentRotation函数:通过解析CSS matrix变换准确获取当前旋转角度,兼容浏览器的自动转换。- 修正
get_degrees函数:调整Math.atan2的参数顺序,移除错误的+100偏移,确保角度计算准确。 - 拖动逻辑优化:拖动开始时获取当前旋转角度作为基础,计算拖动偏移时叠加到基础角度上,实现从当前角度继续旋转。
- 历史记录优化:仅在拖动结束时记录旋转状态,新增redo栈处理重做操作,避免历史记录混乱。
内容的提问来源于stack exchange,提问作者Lyder Prag Sømme
相关产品推荐
相关产品推荐

