我闲着没事浏览 CodePen 的 feed,寻找一些赏心悦目的东西,还没翻到第一页就发现了一个由 Adam Argyle 制作的 简洁的 CSS 悬停效果。
我足足盯着演示看了 10 分钟,惊叹不已。它有一种非常像 app 的感觉。我认为可能是因为它在上下文上非常准确,背景颜色从左侧滑入,然后从右侧滑出。这正是我期望从鼠标移入、移出交互中看到的行为。
无论如何,我打开了一个新的 pen,开始着手重现它。它并不超级复杂,而是巧妙地利用了过渡和变换,并搭配了合适的偏移量。非常优雅!我其实有点尴尬,我花了这么长时间才意识到鼠标移出部分是如何工作的。
下面是我解决它的方法,包括所有细节。
“我敢打赌它使用了 background 上的过渡。”
这是我的第一个想法。定义背景颜色,设置 background-size 和 background-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;
}
是的,就像这样
我已经对链接悬停进行了 ::before 到 scaleX(1) 的过渡。如果我同时将 transform-origin 从 left 反转为 right,那么也许当鼠标移出时,高亮就会从与进入方向相反的方向移出?
a:hover::before {
transform: scaleX(1);
transform-origin: right;
}
🤞
哎呀,反了!我们交换一下 left 和 right 的值。🙃
太棒了。感谢 Adam 的灵感!
这是一个 使用
background-size和线性渐变实现的效果 pen。使用背景来做这件事的优势在于,它适用于可能跨越多行的链接,例如散文中的链接文本。在过去几年中,我在很多项目中都使用过这种效果。太棒了!
我感谢这篇文章展示了思维过程,更重要的是,也展示了哪些方法行不通。
很多作者都喜欢成为房间里最聪明的人,他们的答案都是来自神来之笔,所以看到有人分享我的“往墙上扔东西,向前失败”的方法,真是让人耳目一新。
我的意思是,这里有很多问题。
链接基本上必须是一个按钮。在文本块中使用它会破坏它,尤其是当链接文本换行时。
如果你快速地反复悬停它,过渡会跳到最后,然后重新开始。
… 使用边框,或者更好的方法是使用内阴影,并扩展它们的大小,这可能有助于解决第一个问题。
更好的(更紧凑的)过渡语句可能会帮助解决第二个问题。
酷哥。也许你可以写一篇自己的文章,详细介绍你的思维过程,让它变得更好。我会读的。
我第二个支持,如果你写一篇关于如何优化它,我会读的!我非常想用它,我更希望它能尽可能干净。
Graham 先生,
讨厌的人总会讨厌。我真的很喜欢这篇文章。
有趣的是,你只需要使用背景过渡就能实现原始效果!
诀窍是在
background-size上使用过渡,而不是background-position。这样,你就可以在背景大小动画开始之前更改背景位置,用户将永远无法察觉,因为背景将始终是完全填充或完全为空!:DCodepen: https://codepen.io/sloanfinger/pen/XWePVGj
哈哈,是的。我的第一反应是正确的,但我发现当我的样式开始变得混乱时,我选择了不同的方法。当然,当我还在
transform中挣扎时,我开始意识到我在背景方法中犯了什么错误。这就是事后诸葛亮有趣的地方,也是 CSS 的伟大之处——有太多方法可以实现类似的效果。更有趣的是,我最终使用了与 Adam 相同的方法!或者,你可以使用 before。并交换方向
不错 - 我猜你也可以用内嵌的 box-shadows 来实现。唯一的缺点是,你必须为 box-shadow 的宽度设置一个魔法数字。
正如其他人提到的,使用 `background` 和 `background-position` 绝对是最好的方法。我写了一篇关于 5 种不同变体的文章,大家可能会觉得有用 https://nerdcowboy.com/blog/sliding-underlined-links/
我做了一个类似的东西,但它是对角线。
在 2018 年的这篇文章 中详细解释了。
“动画 `transform`,但突然改变 `transform-origin`” 的策略可以帮助你在 3D 中实现很多很酷的效果,就像 2016 年初的这个演示系列一样
blocks
flair
[这个有一个缺点]https://codepen.io/thebabydino/pen/qZJevJ)
a matter of timing
snake motion
一如既往,Ana,太棒了!感谢分享。
我能挑战你同步改变(字体)颜色吗?!
哇,太棒了。只是想知道这是否可以同时改变字体颜色,以便获得完全的 A11y 颜色对比度,同时仍然看起来很棒。
–bg-h: 这意味着什么?