实现一个有趣的水纹按钮

0 阅读4分钟

今天来实现一个有趣的按钮效果,鼠标悬停在按钮上的时候会有水纹跟随,点击的时候会有一个水纹扩散的效果。当然,这样的动画效果你也可以添加到整个页面或者其他具体的元素上,实现的逻辑都是一样的。

在线体验

码上掘金

codePen

codepen.io/yongtaozhen…

代码实现

html部分

html 部分比较简单,外部是一个 button 按钮,然后在按钮内部加上一个 div 作为水纹容器。

<div class="container">
  <button class="water-ripple-btn" id="rippleBtn">
    <span>水纹按钮</span>
    <div class="ripple-container" id="rippleContainer"></div>
  </button>
</div>

css部分

按钮基本样式

给按钮加上一点简单的样式。,半透明渐变背景(linear-gradient)搭配毛玻璃效果(backdrop-filter) 。

.water-ripple-btn {
  position: relative;
  width: 220px;
  height: 70px;
  border: none;
  border-radius: 10px;
  font-size: 1.2rem;
  font-weight: 600;
  color: white;
  background: linear-gradient(135deg, rgba(59, 130, 246, 0.3), rgba(236, 72, 153, 0.3));
  backdrop-filter: blur(10px);
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
  cursor: pointer;
  overflow: hidden;
  transition: all 0.3s ease;
}

.water-ripple-btn:hover {
  transform: translateY(-3px);
  box-shadow: 0 15px 35px rgba(0, 0, 0, 0.3);
}

悬停水纹样式

.hover-ripple {
  width: 30px;
  height: 30px;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0) 70%);
  box-shadow: 0 0 15px rgba(255, 255, 255, 0.3);
  transition: transform 500ms ease-out, opacity 500ms ease-out;
}

较低的透明度(0.25)和较小的阴影(0 0 15px),避免干扰主体按钮。

水纹扩散样式

.click-ripple {
  background: radial-gradient(circle, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0) 70%);
  box-shadow: 0 0 25px rgba(255, 255, 255, 0.4);
  transform: translate(-50%, -50%) scale(0);
  opacity: 0.4;
  transition: transform 1800ms ease-out, opacity 1800ms ease-out;
}

通过 scale(0)scale(1) 的变换,让水纹从点击点向四周扩散。

JavaScrip部分

创建悬停水纹

btn.addEventListener("mousemove", (e) => {
  if (!isHovering) {
    isHovering = true;
    hoverRipple = document.createElement("div");
    hoverRipple.className = "hover-ripple";
    rippleContainer.appendChild(hoverRipple);
  }

  const rect = btn.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;

  // 更新悬停水纹位置
  hoverRipple.style.left = `${x}px`;
  hoverRipple.style.top = `${y}px`;
  hoverRipple.style.opacity = "1";
  hoverRipple.style.transform = `translate(-50%, -50%) scale(1)`;
});

监听按钮内鼠标移动事件,在首次移动的时候创建悬停水纹元素,计算当前鼠标所在按钮中的相对位置,实时更新悬停水纹位置,保持水纹跟随鼠标。

隐藏悬停水纹

btn.addEventListener("mouseleave", () => {
  if (hoverRipple) {
    hoverRipple.style.opacity = "0";
    hoverRipple.style.transform = `translate(-50%, -50%) scale(0)`;

    setTimeout(() => {
      rippleContainer.removeChild(hoverRipple);
      hoverRipple = null;
      isHovering = false;
    }, 500);
  }
});

监听鼠标离开按钮的事件,在鼠标离开按钮的时候通过 scale(0)opacity: 0 触发水纹缩小并透明化的动画,在动画结束之后移除元素。

点击水纹扩散

btn.addEventListener("click", (e) => {
  const rect = btn.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  const widthDif = Math.max(rect.width - x, x);
  const heightDif = Math.max(rect.height - y, y);
  const radius = Math.max(widthDif, heightDif);
  const createWaterPatterns = () => {
    const ripple = document.createElement("div");
    ripple.className = "click-ripple";
    ripple.style.left = `${x}px`;
    ripple.style.top = `${y}px`;
    ripple.style.width = `${radius * 2 + 20}px`;
    ripple.style.height = `${radius * 2 + 20}px`;
    rippleContainer.appendChild(ripple);

    setTimeout(() => {
      ripple.style.transform = `translate(-50%, -50%) scale(1)`;
    }, 100);
    setTimeout(() => {
      ripple.remove();
    }, 1800);
  };
  createWaterPatterns();
});

监听按钮的点击事件,先计算鼠标到按钮四边的最大距离,以最大距离为扩散水纹的最大半径,确保水纹完成扩散到按钮外部再消失。计算当前鼠标所在按钮中的相对位置,在鼠标点击位置生成一个水纹元素,通过 scale(1) 触发水纹的扩散动画效果。

源码

gitee

gitee.com/zheng_yongt…

github

github.com/yongtaozhen…


  • 🌟 觉得有帮助的可以点个 star~
  • 🖊 有什么问题或错误可以指出,欢迎 pr~
  • 📬 有什么想要实现的功能或想法可以联系我~

公众号

关注公众号『 前端也能这么有趣 』,获取更多有趣内容。

发送 加群 还可以加入群聊,一起来学习(摸鱼)吧~

说在后面

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