ios橡皮筋回弹效果封装(兼容PC,H5)

52 阅读2分钟

先看示例图

screenshots.gif

不允许出父元素

screenshots.gif

只允许横向(或纵向)

screenshots.gif

示例代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>橡皮筋回弹效果示例</title>
    <style>
      style > * {
        margin: 0;
        padding: 0;
      }
      .box_wrapper {
        width: 200px;
        height: 200px;
        border: 1px solid red;
        margin: 0 auto;
        margin-top: 200px;
      }
      #box {
        width: 100px;
        height: 100px;
        background-color: black;
      }
    </style>
  </head>
  <body>
    <div class="box_wrapper">
      <div id="box"></div>
    </div>
    <script>
      function iosRebound(
        element,
        { direction, relativeFather, moveReduce = 4 }
      ) {
        let maxX,
          maxY,
          minX = 0,
          minY = 0;
        let preStartX, preStartY, startX, startY, offsetX, offsetY;

        if (relativeFather) {
          // 相对父元素进行移动
          const parentNode = element.parentNode;

          maxX =
            parentNode.offsetWidth - element.offsetWidth + element.offsetLeft;
          maxY =
            parentNode.offsetHeight - element.offsetHeight + element.offsetTop;

          minX = element.offsetLeft;
          minY = element.offsetTop;
        } else {
          // 相对window
          maxX = window.innerWidth - element.offsetWidth;
          maxY = window.innerHeight - element.offsetHeight;
        }

        /**
         * 开始拖拽
         * @param {Event} e
         */
        function startDrag(e) {
          element.style.transition = "";
          e.preventDefault();

          if (!direction || direction == "x") {
            offsetX = element.offsetLeft;
            preStartX = element.offsetLeft;
            startX = e.type === "mousedown" ? e.clientX : e.touches[0].clientX;
          }

          if (!direction || direction == "y") {
            offsetY = element.offsetTop;
            preStartY = element.offsetTop;
            startY = e.type === "mousedown" ? e.clientY : e.touches[0].clientY;
          }

          const moveEvent = e.type === "mousedown" ? "mousemove" : "touchmove";
          const stopEvent = e.type === "mousedown" ? "mouseup" : "touchend";
          document.addEventListener(moveEvent, drag, { passive: false });
          document.addEventListener(stopEvent, stopDrag);
        }

        /**
         * 拖拽
         * @param {Event} e
         */
        function drag(e) {
          e.preventDefault();
          let x, y;

          if (!direction || direction == "x") {
            const clientX =
              e.type === "mousemove" ? e.clientX : e.touches[0].clientX;

            x = offsetX + (clientX - startX) / moveReduce;
            x = Math.min(Math.max(x, minX), maxX);
            element.style.left = x + "px";
          }

          if (!direction || direction == "y") {
            const clientY =
              e.type === "mousemove" ? e.clientY : e.touches[0].clientY;

            y = offsetY + (clientY - startY) / moveReduce;
            y = Math.min(Math.max(y, minY), maxY);
            element.style.top = y + "px";
          }
        }

        /**
         * 结束拖拽
         */
        function stopDrag() {
          element.style.transition = "0.3s";
          if (!direction || direction == "x") {
            element.style.left = preStartX + "px";
          }
          if (!direction || direction == "y") {
            element.style.top = preStartY + "px";
          }
          if (window.innerWidth <= 1024) {
            document.removeEventListener("touchmove", drag);
            document.removeEventListener("touchend", stopDrag);
          } else {
            document.removeEventListener("mousemove", drag);
            document.removeEventListener("mouseup", stopDrag);
          }
        }

        // 是否是移动端
        const isMobile =
          /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
            navigator.userAgent
          );

        // 样式
        element.style.cursor = "move";
        element.style.position = "absolute";

        element.addEventListener(
          isMobile ? "touchstart" : "mousedown",
          startDrag
        );
      }

      iosRebound(document.getElementById("box"), {
        direction: "",
        relativeFather: false,
        moveReduce: 4,
      });
    </script>
  </body>
</html>

封装代码

/**
 * ios元素橡皮筋回弹效果
 * @param {Element} element 元素
 * @param {Object} [object] 配置对象
 * @param {"x" | "y"} [object.direction] 能够回弹的方向
 * @param {Boolean} [object.relativeFather] 是否根据父元素而不根据window
 * @param {Number} [object.moveReduce] 移动速度减缓
 * @example
 * <div class="box_wrapper">
 *   <div id="box"></div>
 * </div>
 *
 * <style>
 *   * {
 *     margin: 0;
 *     padding: 0;
 *   }
 *   .box_wrapper {
 *     width: 200px;
 *     height: 200px;
 *     border: 1px solid red;
 *     margin: 0 auto;
 *     margin-top: 200px;
 *   }
 *   #box {
 *     width: 100px;
 *     height: 100px;
 *     background-color: black;
 *   }
 * </style>
 *
 * <script>
 *   iosRebound(document.getElementById("box"), {
 *     direction: "y",
 *     relativeFather: true,
 *     moveReduce: 4,
 *   });
 * </script>
 */
function iosRebound(element, { direction, relativeFather, moveReduce = 4 }) {
  if (!element) return console.error("需要元素");
  let maxX,
    maxY,
    minX = 0,
    minY = 0;
  let preStartX, preStartY, startX, startY, offsetX, offsetY;

  if (relativeFather) {
    // 相对父元素进行移动
    const parentNode = element.parentNode;

    maxX = parentNode.offsetWidth - element.offsetWidth + element.offsetLeft;
    maxY = parentNode.offsetHeight - element.offsetHeight + element.offsetTop;

    minX = element.offsetLeft;
    minY = element.offsetTop;
  } else {
    // 相对window
    maxX = window.innerWidth - element.offsetWidth;
    maxY = window.innerHeight - element.offsetHeight;
  }

  /**
   * 开始拖拽
   * @param {Event} e
   */
  function startDrag(e) {
    element.style.transition = "";
    e.preventDefault();

    if (!direction || direction == "x") {
      offsetX = element.offsetLeft;
      preStartX = element.offsetLeft;
      startX = e.type === "mousedown" ? e.clientX : e.touches[0].clientX;
    }

    if (!direction || direction == "y") {
      offsetY = element.offsetTop;
      preStartY = element.offsetTop;
      startY = e.type === "mousedown" ? e.clientY : e.touches[0].clientY;
    }

    const moveEvent = e.type === "mousedown" ? "mousemove" : "touchmove";
    const stopEvent = e.type === "mousedown" ? "mouseup" : "touchend";
    document.addEventListener(moveEvent, drag, { passive: false });
    document.addEventListener(stopEvent, stopDrag);
  }

  /**
   * 拖拽
   * @param {Event} e
   */
  function drag(e) {
    e.preventDefault();
    let x, y;

    if (!direction || direction == "x") {
      const clientX = e.type === "mousemove" ? e.clientX : e.touches[0].clientX;

      x = offsetX + (clientX - startX) / moveReduce;
      x = Math.min(Math.max(x, minX), maxX);
      element.style.left = x + "px";
    }

    if (!direction || direction == "y") {
      const clientY = e.type === "mousemove" ? e.clientY : e.touches[0].clientY;

      y = offsetY + (clientY - startY) / moveReduce;
      y = Math.min(Math.max(y, minY), maxY);
      element.style.top = y + "px";
    }
  }

  /**
   * 结束拖拽
   */
  function stopDrag() {
    element.style.transition = "0.3s";
    if (!direction || direction == "x") {
      element.style.left = preStartX + "px";
    }
    if (!direction || direction == "y") {
      element.style.top = preStartY + "px";
    }
    if (window.innerWidth <= 1024) {
      document.removeEventListener("touchmove", drag);
      document.removeEventListener("touchend", stopDrag);
    } else {
      document.removeEventListener("mousemove", drag);
      document.removeEventListener("mouseup", stopDrag);
    }
  }

  // 是否是移动端
  const isMobile =
    /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
      navigator.userAgent
    );

  // 样式
  element.style.cursor = "move";
  element.style.position = "absolute";

  element.addEventListener(isMobile ? "touchstart" : "mousedown", startDrag);
}