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

相关推荐
掘金者阿豪37 分钟前
把业务数据变成共享仪表盘:Metabase可视化与远程访问实践
前端·后端
kyriewen1 小时前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端1 小时前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员2 小时前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为2 小时前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid2 小时前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger3 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4533 小时前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
lichenyang4533 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端
用户059540174463 小时前
Redis记忆存储故障恢复测试踩坑实录:手动测试让我漏掉了2个一致性Bug
前端·css