实现一个带方向感知的炫酷按钮

191 阅读4分钟

今天来实现一个有意思的按钮效果,可以根据用户鼠标进入和离开的方向动态调整动画过渡效果。

在线体验

码上掘金

codePen

codepen.io/yongtaozhen…

代码实现

html部分

html 部分比较简单,外部是一个 button 按钮,然后在按钮内部加上四个 div 元素作为覆盖层,用于实现不同方向的渐变效果。

<button class="direction-aware-btn" id="directionBtn">
  <div class="btn-overlay from-top"></div>
  <div class="btn-overlay from-right"></div>
  <div class="btn-overlay from-bottom"></div>
  <div class="btn-overlay from-left"></div>
  <span class="btn-content">方向感知</span>
</button>

css部分

按钮基本样式

给按钮加上一点简单的样式。

direction-aware-btn {
  position: relative;
  width: 220px;
  height: 70px;
  border-radius: 10px;
  font-size: 1.2rem;
  font-weight: 600;
  color: white;
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  cursor: pointer;
  overflow: hidden;
  transition: all 0.3s ease;
  box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3);
}
.direction-aware-btn:hover {
  transform: translateY(-5px);
  box-shadow: 0 15px 30px -5px rgba(0, 0, 0, 0.4);
}

覆盖层基本样式

.btn-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 101%;
  height: 101%;
  z-index: -1;
  transition: transform 0.5s ease;
}

关键的代码是transition: transform 0.5s ease;这个动画,transform 属性发生变化时,由于设置了过渡属性,这个变化不会瞬间完成,而是在 0.5 秒内平滑过渡;时间函数 ease 是最常用的缓动效果,它的速度曲线类似于:

  • 开始时:动画加速
  • 中间时:动画保持较快速度
  • 结束时:动画减速

上覆盖层样式

.from-top {
  background: linear-gradient(to bottom, #3b82f6, #8b5cf6);
  transform: translateY(-101%);
}

右覆盖层样式

.from-right {
  background: linear-gradient(to left, #8b5cf6, #ec4899);
  transform: translateX(101%);
}

下覆盖层样式

.from-bottom {
  background: linear-gradient(to top, #ec4899, #f59e0b);
  transform: translateY(101%);
}

左覆盖层样式

.from-left {
  background: linear-gradient(to right, #f59e0b, #3b82f6);
  transform: translateX(-101%);
}

按钮hover样式

.direction-aware-btn:hover .from-top.active,
.direction-aware-btn:hover .from-right.active,
.direction-aware-btn:hover .from-bottom.active,
.direction-aware-btn:hover .from-left.active {
  transform: translate(0, 0);
}

translate(0, 0) 将覆盖层的定位重置到按钮内部,形成一个动画过渡效果。

获取按钮定位

计算鼠标在按钮内的相对位置

const rect = btn.getBoundingClientRect();
const x = e.clientX - rect.left; // 鼠标在按钮内的X坐标
const y = e.clientY - rect.top; // 鼠标在按钮内的Y坐标

计算到按钮四条边的距离

// 计算到四条边的距离
const distToTop = y;
const distToRight = rect.width - x;
const distToBottom = rect.height - y;
const distToLeft = x;

找出最小距离对应的边

// 找出最小距离,确定方向
const minDist = Math.min(
  distToTop,
  distToRight,
  distToBottom,
  distToLeft
);
if (minDist === distToTop) {
  return "top";
} else if (minDist === distToRight) {
  return "right";
} else if (minDist === distToBottom) {
  return "bottom";
}
return "left";

鼠标进入按钮

btn.addEventListener("mouseenter", (e) => {
  resetOverlays();
  const direction = getDirection(e);

  // 设置对应覆盖层的active类
  const activeBtn = btn.querySelector(`.from-${direction}`);
  activeBtn.classList.add("active");
});

监听按钮的mouseenter事件,鼠标进入按钮后先获取到鼠标进入的方向,然后给对于方位的覆盖层加上active类,触发对应覆盖层的过渡动画。

鼠标离开按钮

监听按钮移出事件

btn.addEventListener("mouseleave", (e) => {
  ……………………
});

获取当前激活的覆盖层

const activeBtn = btn.querySelector(".btn-overlay.active");

获取鼠标移出的方向

const direction = getDirection(e);

动画过渡向鼠标移出方向

const directionMap = {
  top: "translateY(-101%)",
  right: "translateX(101%)",
  bottom: "translateY(101%)",
  left: "translateX(-101%)",
};
activeBtn.style.transform = directionMap[direction];

覆盖层定位重置

const classList = activeBtn.classList;
const directionClass = [...classList].find((cls) =>
  cls.startsWith("from-")
);
const directionName = directionClass.replace("from-", "");
setTimeout(() => {
  activeBtn.style.transition = "none";
  activeBtn.style.transform = directionMap[directionName];
  activeBtn.classList.remove("active");
  setTimeout(() => {
    activeBtn.style.transition = "transform 0.5s ease";
    activeBtn.style.removeProperty("transform");
  }, 50);
}, 500);

因为前面transition动画设置的时间是0.5s,所以我们在动画结束(500ms)后将覆盖层的定位重置回最初的位置。重置的时候需要先将transition动画去掉,在重置完成后再重新加上过渡动画。

源码

gitee

gitee.com/zheng_yongt…

github

github.com/yongtaozhen…


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

公众号

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

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

说在后面

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