Adam Argyle 的酷炫鼠标移出 CSS 悬停效果

Avatar of Geoff Graham
Geoff Graham

DigitalOcean 为您的旅程各个阶段提供云产品。立即开始使用 价值 200 美元的免费积分!

我闲着没事浏览 CodePen 的 feed,寻找一些赏心悦目的东西,还没翻到第一页就发现了一个由 Adam Argyle 制作的 简洁的 CSS 悬停效果

我足足盯着演示看了 10 分钟,惊叹不已。它有一种非常像 app 的感觉。我认为可能是因为它在上下文上非常准确,背景颜色从左侧滑入,然后从右侧滑出。这正是我期望从鼠标移入、移出交互中看到的行为。

无论如何,我打开了一个新的 pen,开始着手重现它。它并不超级复杂,而是巧妙地利用了过渡和变换,并搭配了合适的偏移量。非常优雅!我其实有点尴尬,我花了这么长时间才意识到鼠标移出部分是如何工作的。

下面是我解决它的方法,包括所有细节。

“我敢打赌它使用了 background 上的过渡。”

这是我的第一个想法。定义背景颜色,设置 background-sizebackground-position,然后对 background-position 进行过渡。过去我经常用这种方法实现“扩展”背景颜色。我自己在一些项目中也使用过这种方法,比如这个

如果我能做同样的事情,只是从左到右,那么剩下的就只是鼠标移出,对吧?不。问题在于,没有方法可以真正让 background-position 从左到右再到左到右进行过渡。我可以让它做其中一个,但不能两个都做。

“也许它是一个 transform 而不是 background。”

我的下一个尝试是转向变换。transform 属性提供了很多函数,可以将它们组合在一起进行过渡,从而实现稍微复杂的运动。例如,background 可以通过改变元素的 scale() 来“扩展”或“收缩”。或者,在这种情况下,可以通过 scaleX() 仅沿 x 轴进行缩放。

但是就像我提到的,没有办法将元素的 background 隔离出来进行这种操作。从 scaleX(0)scaleX(1) 会缩放整个元素,因此它基本上会将链接(包括内容)压缩成零,然后将其拉伸回其自然大小,这完全是不同的效果。此外,这意味着从 scaleX(0) 开始,默认情况下会隐藏整个东西,使其无法使用。

但是伪元素可以工作!它是否会被压缩或隐藏并不重要,因为它不是实际内容的一部分。我们必须将 background 放到它上面,并将其直接放在链接的下方。

a {
  /* Keeps the pseudo-element contained to the element */
  position: relative;
}

a::before {
  background: #ff9800;
  content: "";
  inset: 0; /* Logical equivalent to physical offsets */
  position: absolute;
  transform: scaleX(0); /* Hide by default */
  z-index: -1; /* Ensures the link is stacked on top */
}

“现在我需要 ::before 在悬停时发生变化。”

我知道我可以通过将 ::before 与链接元素的 :hover 状态链接起来,让它从 0 缩放至 1

a:hover::before {
  transform: scaleX(1)
}

不错!我找到了一些东西。

在上面撒一点 transition 魔法粉,事情就开始变得生动起来。

a::before {
  background: #ff9800;
  content: "";
  inset: 0;
  position: absolute;
  transform: scaleX(0);
  transition: transform .5s ease-in-out;
  z-index: -1;
}

“嗯,过渡在两个方向都移动。”

再次,这是我有点卡住的地方。我脑子里的某个地方就是卡住了,不知道为什么。像往常一样,我跑到 CSS-Tricks 年鉴 上查看是否有遗漏的属性。

啊,是的。应该是 transform-origin。它允许我设置 transform 开始的位置,这与之前尝试设置 background-position 并不完全不同。transform 可以从左侧开始,而不是其默认的 50% 50% 位置。

a::before {
  background: #ff9800;
  content: "";
  inset: 0;
  position: absolute;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .5s ease-in-out;
  z-index: -1;
}

是的,就像这样

我已经对链接悬停进行了 ::beforescaleX(1) 的过渡。如果我同时将 transform-originleft 反转为 right,那么也许当鼠标移出时,高亮就会从与进入方向相反的方向移出?

a:hover::before {
  transform: scaleX(1);
  transform-origin: right;
}

🤞

哎呀,反了!我们交换一下 leftright 的值。🙃

太棒了。感谢 Adam 的灵感!