今天来实现一个有意思的按钮效果,可以根据用户鼠标进入和离开的方向动态调整动画过渡效果。
在线体验
码上掘金
codePen
代码实现
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
github
- 🌟 觉得有帮助的可以点个 star~
- 🖊 有什么问题或错误可以指出,欢迎 pr~
- 📬 有什么想要实现的功能或想法可以联系我~
公众号
关注公众号『 前端也能这么有趣 』,获取更多有趣内容。
发送 加群 还可以加入群聊,一起来学习(摸鱼)吧~
说在后面
🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『
前端也能这么有趣
』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。