怎么写一个可以鼠标控制旋转的div?

说在前面

鼠标控制元素旋转在现在也是一个很常见的功能,让我们从实现div元素的旋转控制开始来了解元素旋转的具体原理和实现方法吧。

效果展示

体验地址

code.juejin.cn/pen/7290719...

实现步骤

画一个div

首先我们需要先画一个div,并在它上方加上旋转图标,我们可以通过这个图标来对div进行旋转,具体代码如下:

html 复制代码
<!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">&#x21bb;</div>
      </div>
    </div>
  </body>
  <script src="./index.js"></script>
</html>

加点css

将div设置为红色背景的方块,调整旋转图标的位置,具体代码如下:

css 复制代码
.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、获取方块和旋转图标元素对象

首先我们要先获取方块和旋转图标元素对象,便于后续事件监听和元素操作。

javascript 复制代码
const rotateDiv = document.querySelector(".rotate-div");
const rotateIcon = document.querySelector(".rotate-icon");

2、编写方块旋转逻辑

(1)获取方块中心点

getBoundingClientRect

getBoundingClientRect用于获取某个元素相对于视窗的位置集合。集合中有top, right, bottom, left等属性。

ini 复制代码
rectObject = object.getBoundingClientRect();

返回值类型:TextRectangle对象,每个矩形具有四个整数性质( 上, 右 , 下,和左 )表示的坐标的矩形,以像素为单位。

rectObject.top:元素上边到视窗上边的距离;

rectObject.right:元素右边到视窗左边的距离;

rectObject.bottom:元素下边到视窗上边的距离;

rectObject.left:元素左边到视窗左边的距离;

我们记录下方块的初始中心点:

javascript 复制代码
 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)

arduino 复制代码
Math.atan2(y, x)

y, x

atan2 方法返回一个 -pi 到 pi 之间的数值,表示点 (x, y) 对应的偏移角度。这是一个逆时针角度,以弧度为单位,正 X 轴和点 (x, y) 与原点连线 之间。注意此函数接受的参数:先传递 y 坐标,然后是 x 坐标。

atan2 接受单独的 x 和 y 参数,而 atan 接受两个参数的比值。

由于 atan2Math 的静态方法,所以应该像这样使用:Math.atan2(),而不是作为你创建的 Math 实例的方法。

javascript 复制代码
function getAngle(centerX, centerY, mouseX, mouseY) {
  return Math.atan2(mouseY - centerY, mouseX - centerX) * (180 / Math.PI);
}

使用当前鼠标位置相对角度减去鼠标初始点击点的相对角度即可得到鼠标旋转的角度。

javascript 复制代码
startingMouseAngle = getAngle(centerX, centerY, event.clientX, event.clientY);
const deltaMouseAngle = currentMouseAngle - startingMouseAngle;
(3)旋转角度简化

方块的最大旋转角度为360度,所以我们对角度进行取模,保持旋转角度在360度以内即可。

javascript 复制代码
function normalizeRotation(rotation) {
  if (rotation >= 0) {
    return rotation % 360;
  } else {
    return (rotation % 360) + 360;
  }
}
(4)给方块设置旋转角度
javascript 复制代码
rotateDiv.style.transform = `rotate(${newRotation}deg)`;

3、移除旋转逻辑

鼠标抬起的时候我们应该将旋转逻辑给移除,及将鼠标移动和抬起事件移除。

javascript 复制代码
function stopSpin() {
  window.removeEventListener("mousemove", spin);
  window.removeEventListener("mouseup", stopSpin);
}

4、完整代码

完整的JavaScrip代码如下:

javascript 复制代码
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,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

相关推荐
马船长26 分钟前
RCE-PLUS (学习记录)
java·linux·前端
轻口味30 分钟前
【每日学点鸿蒙知识】webview性能优化、taskpool、热更新、Navigation问题、调试时每次都卸载重装问题
javascript·list·harmonyos
学前端的小朱1 小时前
修改输出资源的名称和路径、自动清空上次打包资源
前端·webpack·打包工具
涔溪1 小时前
如何在Express.js中定义多个HTTP方法?
javascript·http·express
嘤嘤怪呆呆狗1 小时前
【开发问题记录】执行 git cz 报require() of ES Module…… 错误
前端·javascript·vue.js·git·vue
夜斗(dou)2 小时前
谷歌开发者工具 - 网络篇
前端·网络·chrome devtools
常常不爱学习2 小时前
CSS盒子模型(溢出隐藏,块级元素和行级元素的居中对齐,元素样式重置)
前端·css
风抽过的烟头2 小时前
Python提取字符串中的json,时间,特定字符
前端·python·json
SomeB1oody2 小时前
【Rust自学】6.3. 控制流运算符-match
开发语言·前端·rust
夕水2 小时前
你可能需要避免的5个react的ref错误用法
javascript·react.js