前端-鼠标位置为中心滚轮缩放或拖拽元素

111 阅读1分钟

Document - Google Chrome 2022-11-10 16-40-44.gif

<!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>

    <style>
      .container {
        width: 500px;
        height: 500px;
        margin: auto;
        overflow: hidden;
        border: 1px solid;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <video
        id="videoEl"
        autoplay
        controls
        width="500"
        height="500"
        src="https://v26-web.douyinvod.com/18f2b18702db6744e33d7cc87f731bdf/636cc632/video/tos/cn/tos-cn-ve-15c001-alinc2/b21ddffbd734459f931ae2289eeb5449/?a=6383&ch=5&cr=3&dr=0&lr=all&cd=0%7C0%7C0%7C3&cv=1&br=1692&bt=1692&cs=0&ds=6&ft=rVWEerwwZRcZseFo3PDS6kFgAX1tG0ynnS9eFEj5tJr12ni7t&mime_type=video_mp4&qs=0&rc=OWZpOTg3aGZmZzU0OjQ2M0BpM3lkbzQ6ZjNtZjMzNGkzM0BgY2EuLi0yXl8xLV8wLTQwYSNpc15scjRfb2JgLS1kLS9zcw%3D%3D&l=2022111016363401015015410227004C29"
      ></video>
    </div>
    <div class="log"></div>

    <script type="text/javascript">
      // 获取dom
      const container = document.querySelector(".container");
      const videoEl = document.getElementById("videoEl");
      const log = document.querySelector(".log");
      // 全局变量
      let result = {},
        x = 0,
        y = 0,
        scale = 1,
        minScale = 1,
        maxScale = 4,
        isPointerdown = false, // 按下标识
        diff = { x: 0, y: 0 }, // 相对于上一次pointermove移动差值
        lastPointermove = { x: 0, y: 0 }; // 计算diff

      const getSize = (videoWidth, videoHeight, maxWidth, maxHeight) => {
        const videoRatio = videoWidth / videoHeight;
        const maxRatio = maxWidth / maxHeight;
        let width, height;
        // video实际宽高比例 >= 显示宽高比例
        if (videoRatio >= maxRatio) {
          if (videoWidth > maxWidth) {
            width = maxWidth;
            height = (maxWidth / videoWidth) * videoHeight;
          } else {
            width = videoWidth;
            height = videoHeight;
          }
        } else {
          if (videoHeight > maxHeight) {
            width = (maxHeight / videoHeight) * videoWidth;
            height = maxHeight;
          } else {
            width = videoWidth;
            height = videoHeight;
          }
        }
        return { width: width, height: height };
      };

      result = getSize(
        videoEl.width,
        videoEl.height,
        container.clientWidth,
        container.clientHeight
      );
      // 滚轮缩放
      const wheelZoom = () => {
        container.addEventListener("wheel", (e) => {
          let ratio = 1.1;
          // 缩小
          if (e.deltaY > 0) {
            ratio = 1 / 1.1;
          }
          // 限制缩放倍数
          const _scale = scale * ratio;
          if (_scale > maxScale) {
            ratio = maxScale / scale;
            scale = maxScale;
          } else if (_scale < minScale) {
            ratio = minScale / scale;
            scale = minScale;
          } else {
            scale = _scale;
          }
          console.log(ratio);

          // 目标元素是 video 说明鼠标在 video 上,以鼠标位置为缩放中心,否则默认以视频中心点为缩放中心
          if (e.target.tagName === "VIDEO") {
            const origin = {
              x: (ratio - 1) * result.width * 0.5,
              y: (ratio - 1) * result.height * 0.5,
            };
            // 计算偏移量
            x -= (ratio - 1) * (e.offsetX - x) - origin.x;
            y -= (ratio - 1) * (e.offsetY - y) - origin.y;
          }
          videoEl.style.transform =
            "translate3d(" + x + "px, " + y + "px, 0) scale(" + scale + ")";
          if (scale === 1) {
            videoEl.style.transform = "scale(" + scale + ")";
          }
          log.innerHTML = `x = ${x.toFixed(0)}<br>y = ${y.toFixed(
            0
          )}<br>scale = ${scale.toFixed(5)}`;
          e.preventDefault();
        });
      };

      // 拖拽
      const drag = () => {
        videoEl.addEventListener("pointerdown", (e) => {
          isPointerdown = true;
          lastPointermove = { x: e.clientX, y: e.clientY };
        });

        videoEl.addEventListener("pointermove", (e) => {
          if (isPointerdown) {
            const current1 = { x: e.clientX, y: e.clientY };
            diff.x = current1.x - lastPointermove.x;
            diff.y = current1.y - lastPointermove.y;
            lastPointermove = { x: current1.x, y: current1.y };
            x += diff.x;
            y += diff.y;
            videoEl.style.transform = `translate3d(${x}px,${y}px, 0) scale(${scale})`;
            log.innerHTML = `x = ${x.toFixed(0)}<br>y = ${y.toFixed(
              0
            )}<br>scale = ${scale.toFixed(5)}`;
          }
          e.preventDefault();
        });

        videoEl.addEventListener("pointerup", (e) => {
          if (isPointerdown) {
            isPointerdown = false;
          }
        });
      };
      drag();
      wheelZoom();
    </script>
  </body>
</html>