点击水波纹效果

217 阅读1分钟
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <title></title>
  </head>

  <body>
    <div id="admin">
      <div id="btn"></div>
    </div>
    <script type="text/javascript">
      document.querySelector("#btn").onmousedown = function (event) {
        const container = this;

        const { x, y, centerX, centerY, size } = computeRippleStyles(
          container,
          event
        );
        console.log(x, y, centerX, centerY, size);
        const ripple = document.createElement("div");
        ripple.classList.add("my-ripple");
        ripple.style.opacity = `0`;
        ripple.style.transform = `translate(${x}px, ${y}px) scale3d(.3, .3, .3)`; //
        ripple.style.width = `${size}px`;
        ripple.style.height = `${size}px`;
        // 记录水波的创建时间
        ripple.dataset.createdAt = String(performance.now());

        const { position } = window.getComputedStyle(container);
        container.style.overflow = "hidden";
        position === "static" && (this.style.position = "relative");
        console.log(ripple);
        container.appendChild(ripple);

        window.setTimeout(() => {
          ripple.style.transform = `translate(${centerX}px, ${centerY}px) scale3d(1, 1, 1)`;
          ripple.style.opacity = `.25`;
        });
      };
      document.querySelector("#btn").onmouseup = function (event) {
        const container = this;
        const ripples = container.querySelectorAll(".my-ripple");
        if (!ripples.length) {
          return;
        }

        const lastRipple = ripples[ripples.length - 1];
        // 通过水波的创建时间计算出扩散动画还需要执行多久,确保每一个水波都完整的执行了扩散动画
        const delay =
          300 - performance.now() + Number(lastRipple.dataset.createdAt);

        setTimeout(() => {
          lastRipple.style.opacity = `0`;

          setTimeout(() => lastRipple.parentNode?.removeChild(lastRipple), 300);
        }, delay);
      }
      function computeRippleStyles(element, event) {
        const { top, left } = element.getBoundingClientRect();
        const { clientWidth, clientHeight } = element;

        const radius = Math.sqrt(clientWidth ** 2 + clientHeight ** 2) / 2;
        const size = radius * 2;
        console.log(top, left, clientWidth, clientHeight, size);
        const localX = event.clientX - left;
        const localY = event.clientY - top;

        const centerX = (clientWidth - radius * 2) / 2;
        const centerY = (clientHeight - radius * 2) / 2;

        const x = localX - radius;
        const y = localY - radius;

        return { x, y, centerX, centerY, size };
      }

    </script>
  </body>
  <style>
    #btn {
      margin: 30px 40px;
      width: 80px;
      height: 60px;
      border: 1px solid black;
    }
    .my-ripple {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 100;
      border-radius: 50%;
      background-color: currentColor;
      opacity: 0;
      transition: transform 0.2s cubic-bezier(0.68, 0.01, 0.62, 0.6),
        opacity 0.08s linear;
      will-change: transform, opacity;
      pointer-events: none;
    }
  </style>
</html>