说在前面
鼠标控制元素旋转在现在也是一个很常见的功能,让我们从实现div元素的旋转控制开始来了解元素旋转的具体原理和实现方法吧。
效果展示
体验地址
https://code.juejin.cn/pen/7290719197439459386
实现步骤
画一个div
首先我们需要先画一个div,并在它上方加上旋转图标,我们可以通过这个图标来对div进行旋转,具体代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <link rel="stylesheet" href="./index.css" /> </head> <body> <div class="container"> <div class="rotate-div"> <div class="rotate-icon">↻</div> </div> </div> </body> <script src="./index.js"></script> </html>
加点css
将div设置为红色背景的方块,调整旋转图标的位置,具体代码如下:
.container { display: flex; justify-content: center; align-items: center; height: 100vh; } .rotate-div { position: relative; width: 200px; height: 200px; background-color: red; transform-origin: center center; } .rotate-icon { position: absolute; top: -50px; /* 调整图标的位置 */ left: 50%; transform: translateX(-50%); font-size: 20px; cursor: pointer; }
效果如下:
完成鼠标拖拽旋转功能
鼠标在旋转图标按下的时候,我们需要监听鼠标移动事件,根据鼠标移动位置和初始点击位置的相对角度来计算方块旋转的角度。
1、获取方块和旋转图标元素对象
首先我们要先获取方块和旋转图标元素对象,便于后续事件监听和元素操作。
const rotateDiv = document.querySelector(".rotate-div"); const rotateIcon = document.querySelector(".rotate-icon");
2、编写方块旋转逻辑
(1)获取方块中心点
getBoundingClientRect
getBoundingClientRect用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性。
rectObject = object.getBoundingClientRect();
返回值类型:TextRectangle对象,每个矩形具有四个整数性质( 上, 右 , 下,和左 )表示的坐标的矩形,以像素为单位。
rectObject.top:元素上边到视窗上边的距离;
rectObject.right:元素右边到视窗左边的距离;
rectObject.bottom:元素下边到视窗上边的距离;
rectObject.left:元素左边到视窗左边的距离;
我们记录下方块的初始中心点:
const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2;
(2)计算旋转角度
Math.atan2()
Math.atan2()
返回从原点 (0,0) 到 (x,y) 点的线段与 x 轴正方向之间的平面角度 (弧度值),也就是 Math.atan2(y,x)
Math.atan2(y, x)
atan2
方法返回一个 -pi 到 pi 之间的数值,表示点 (x, y) 对应的偏移角度。这是一个逆时针角度,以弧度为单位,正 X 轴和点 (x, y) 与原点连线 之间。注意此函数接受的参数:先传递 y 坐标,然后是 x 坐标。
atan2
接受单独的 x 和 y 参数,而 atan
接受两个参数的比值。
由于 atan2
是 Math
的静态方法,所以应该像这样使用:Math.atan2()
,而不是作为你创建的 Math
实例的方法。
function getAngle(centerX, centerY, mouseX, mouseY) { return Math.atan2(mouseY - centerY, mouseX - centerX) * (180 / Math.PI); }
使用当前鼠标位置相对角度减去鼠标初始点击点的相对角度即可得到鼠标旋转的角度。
startingMouseAngle = getAngle(centerX, centerY, event.clientX, event.clientY); const deltaMouseAngle = currentMouseAngle - startingMouseAngle;
(3)旋转角度简化
方块的最大旋转角度为360度,所以我们对角度进行取模,保持旋转角度在360度以内即可。
function normalizeRotation(rotation) { if (rotation >= 0) { return rotation % 360; } else { return (rotation % 360) + 360; } }
(4)给方块设置旋转角度
rotateDiv.style.transform = `rotate(${newRotation}deg)`;
3、移除旋转逻辑
鼠标抬起的时候我们应该将旋转逻辑给移除,及将鼠标移动和抬起事件移除。
function stopSpin() { window.removeEventListener("mousemove", spin); window.removeEventListener("mouseup", stopSpin); }
4、完整代码
完整的JavaScrip代码如下:
const rotateDiv = document.querySelector(".rotate-div"); const rotateIcon = document.querySelector(".rotate-icon"); let startingMouseAngle = 0; let startingRotation = 0; rotateIcon.addEventListener("selectstart", function (event) { event.preventDefault(); }); rotateIcon.addEventListener("mousedown", function (event) { const rect = rotateDiv.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; startingMouseAngle = getAngle(centerX, centerY, event.clientX, event.clientY); startingRotation = getCurrentRotation(); window.addEventListener("mousemove", spin); window.addEventListener("mouseup", stopSpin); }); function stopSpin() { window.removeEventListener("mousemove", spin); window.removeEventListener("mouseup", stopSpin); } function spin(event) { const rect = rotateDiv.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const currentMouseAngle = getAngle( centerX, centerY, event.clientX, event.clientY ); const deltaMouseAngle = currentMouseAngle - startingMouseAngle; let newRotation = startingRotation + deltaMouseAngle; newRotation = normalizeRotation(newRotation); rotateDiv.style.transform = `rotate(${newRotation}deg)`; } function normalizeRotation(rotation) { if (rotation >= 0) { return rotation % 360; } else { return (rotation % 360) + 360; } } function getAngle(centerX, centerY, mouseX, mouseY) { return Math.atan2(mouseY - centerY, mouseX - centerX) * (180 / Math.PI); } function getCurrentRotation() { const transformStyle = window .getComputedStyle(rotateDiv) .getPropertyValue("transform"); const matrix = new DOMMatrixReadOnly(transformStyle); const angle = Math.acos(matrix.a) * (180 / Math.PI); return matrix.b < 0 ? -angle : angle; }
说在后面
🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。