前端的拖拽和缩放(缩放以鼠标为中心)

277 阅读2分钟

效果:

code.juejin.cn/pen/7383912…

代码具体实现如下:

但是有几个注意点

(1)为什么需要设置 transform-origin: 0 0;

缩放时以鼠标为中心进行缩放。这意味着需要手动计算缩放过程中元素的位移,以确保缩放是以鼠标为中心的。如果不设置 transform-origin,缩放和位移的计算将变得更加复杂。

设置 transform-origin: 0 0; 后,所有的位移和缩放都是基于左上角进行的,这使得计算变换的位移量更加直观和简单。只需要考虑从左上角开始的平移和缩放,而不需要考虑元素的中心点。

如果不设置 transform-origin: 0 0;,在缩放和位移时需要考虑变换原点的位置,这会增加计算的复杂性。

(2)关于代码中的(scale / prevScale - 1)

在缩放过程中需要计算新的平移值,使得缩放以鼠标为中心。(scale / prevScale - 1) 计算出相对于原始缩放比例的变化量。

例如,如果 scale 增加了10%,那么 scale / prevScale 会是1.1,减去1后得到0.1,表示增加的10%。

offsetX * (scale / prevScale - 1) 计算出由于缩放而导致的水平偏移量。类似地,offsetY * (scale / prevScale - 1) 计算出由于缩放而导致的垂直偏移量。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>移动和缩放容器</title>
    <style>
      body {
        margin: 0;
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        background-color: #f0f0f0;
        overflow: hidden;
      }
      #container {
        width: 200px;
        height: 200px;
        background-color: #4caf50;
        cursor: grab;
        user-select: none;
        transform-origin: 0 0; /* 设置原点为左上角 */
      }
    </style>
  </head>
  <body>
    <div id="container">ABC</div>

    <script>
      const container = document.getElementById("container");
      let isDragging = false;
      let startX, startY, initialX, initialY;
      let scale = 1;
      let translateX = 0,
        translateY = 0;

      container.addEventListener("mousedown", (e) => {
        isDragging = true;
        startX = e.clientX;
        startY = e.clientY;
        initialX = translateX;
        initialY = translateY;
        container.style.cursor = "grabbing";
      });

      document.addEventListener("mousemove", (e) => {
        if (isDragging) {
          const dx = e.clientX - startX;
          const dy = e.clientY - startY;
          translateX = initialX + dx;
          translateY = initialY + dy;
          container.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
        }
      });

      document.addEventListener("mouseup", () => {
        isDragging = false;
        container.style.cursor = "grab";
      });

      container.addEventListener("wheel", (e) => {
        e.preventDefault();
        const minScale = 0.5;
        const maxScale = 3;
        const rect = container.getBoundingClientRect();
        const offsetX = e.clientX - rect.left;
        const offsetY = e.clientY - rect.top;

        const prevScale = scale;
        const delta = e.deltaY || e.detail || e.wheelDelta;
        // console.log(delta, "delta");
        scale += delta * -0.01;
        scale = Math.min(Math.max(minScale, scale), maxScale);

        const newX = offsetX * (scale / prevScale - 1);
        const newY = offsetY * (scale / prevScale - 1);

        console.log(newX);

        translateX -= newX;
        translateY -= newY;

        container.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
      });
    </script>
  </body>
</html>