使用背景裁剪、蒙版和 3D 的酷炫 CSS 悬停效果

Avatar of Temani Afif
Temani Afif

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

我们已经完成了一系列关于 CSS 悬停效果的有趣方法的文章。 我们从 使用 CSS background 属性的许多示例 开始,然后转向 text-shadow 属性,我们实际上没有使用任何阴影。 我们还将它们与 CSS 变量和 calc() 结合使用,以优化代码并使其易于管理。

在本文中,我们将从这两篇文章出发,创建更复杂的 CSS 悬停动画。 我们正在讨论背景裁剪、CSS 蒙版,甚至开始接触 3D 透视。 换句话说,我们将探索更高级的技术,并挑战 CSS 在悬停效果方面能做到的极限!

酷炫的悬停效果系列

  1. 使用背景属性的酷炫悬停效果
  2. 使用 CSS 文本阴影的酷炫悬停效果
  3. 使用背景裁剪、蒙版和 3D 的酷炫悬停效果 (您就在这里!)

这里只是我们制作内容的简单示例

使用 background-clip 的悬停效果

让我们谈谈 background-clip。 此 CSS 属性接受 text 关键字值,它允许我们将渐变应用于元素的文本,而不是实际的背景

因此,例如,我们可以像使用 color 属性一样更改悬停时的文本颜色,但这种方式可以使颜色变化进行动画处理

我所做的只是在元素中添加 background-clip: text 并使用 transitionbackground-position 进行动画处理。 不需要比这更复杂!

但如果我们将多个具有不同背景裁剪值的渐变组合在一起,我们可以做得更好。

在那个示例中,我使用了两种不同的渐变和两种具有 background-clip 的值。 第一个背景渐变被裁剪到文本 (由于 text 值) 以设置悬停时的颜色,而第二个背景渐变创建底部下划线 (由于 padding-box 值)。 其他所有内容都直接从 本系列第一篇文章中我们所做的工作 复制而来。

如何制作一种悬停效果,使条形从上到下滑动,看起来像文本被扫描,然后着色?

这次我改变了第一个渐变的大小来创建线条。 然后我用另一个更新文本颜色的渐变来滑动它,以产生这种错觉! 您可以在此示例中看到正在发生的事情

我们只是触及了 background-clip 能力所能做到的表面! 但是,此技术可能是您想要避免在生产中使用的技术,因为众所周知,Firefox 在与 background-clip 相关的 许多已报告的错误 中存在问题。 Safari 也存在支持问题。 这就只剩下 Chrome 对这些内容提供了可靠的支持,因此在我们继续时,也许可以打开 Chrome。

让我们继续使用 background-clip 的另一个悬停效果

您可能认为这个看起来超级简单,与我们刚刚介绍的内容相比——而且您是对的,这里没有什么花哨的。 我只是滑动了一个渐变,同时增加了另一个渐变的大小。

但我们在这里是为了研究高级悬停效果,对吧? 让我们稍微改变一下,使动画在鼠标光标离开元素时有所不同。 相同的悬停效果,但动画的结尾不同

很酷,对吧? 让我们分析一下代码

.hover {
  --c: #1095c1; /* the color */

  color: #0000;
  background: 
    linear-gradient(90deg, #fff 50%, var(--c) 0) calc(100% - var(--_p, 0%)) / 200%, 
    linear-gradient(var(--c) 0 0) 0% 100% / var(--_p, 0%) no-repeat,
    var(--_c, #0000);
  -webkit-background-clip: text, padding-box, padding-box;
          background-clip: text, padding-box, padding-box;
  transition: 0s, color .5s, background-color .5s;
}
.hover:hover {
  color: #fff;
  --_c: var(--c);
  --_p: 100%;
  transition: 0.5s, color 0s .5s, background-color 0s .5s;
}

我们有三个背景层——两个渐变和使用 --_c 变量定义的 background-color,该变量最初设置为透明 (#0000)。 悬停时,我们将颜色更改为白色,并将 --_c 变量更改为主颜色 (--c)。

以下是该 transition 中发生的事情:首先,我们将转换应用于所有内容,但我们将 colorbackground-color 延迟 0.5s,以创建滑动效果。 紧随其后,我们更改了 colorbackground-color。 您可能注意到没有视觉变化,因为文本已经是白色 (由于第一个渐变) 并且背景已经设置为主要颜色 (由于第二个渐变)。

然后,在鼠标移出时,我们将立即更改所有内容 (注意 0s 延迟),除了具有转换的 colorbackground-color。 这意味着我们将所有渐变恢复到其初始状态。 同样,您可能看不到视觉变化,因为文本 colorbackground-color 已经在悬停时更改了。

最后,我们将淡入淡出应用于颜色和 background-color,以创建动画的鼠标移出部分。 我知道,这可能很难理解,但您可以通过使用不同的颜色更好地可视化这种技巧

将鼠标悬停在上面很多次,您将看到悬停时正在进行动画处理的属性以及鼠标移出时正在进行动画处理的属性。 然后,您可以了解我们如何为相同的悬停效果创建两种不同的动画。

让我们不要忘记 在本系列之前文章中使用的 DRY 切换技术,通过只使用一个变量进行切换来帮助减少代码量

.hover {
  --c: 16 149 193; /* the color using the RGB format */

  color: rgb(255 255 255 / var(--_i, 0));
  background:
    /* Gradient #1 */
    linear-gradient(90deg, #fff 50%, rgb(var(--c)) 0) calc(100% - var(--_i, 0) * 100%) / 200%,
    /* Gradient #2 */
    linear-gradient(rgb(var(--c)) 0 0) 0% 100% / calc(var(--_i, 0) * 100%) no-repeat,
    /* Background Color */
    rgb(var(--c)/ var(--_i, 0));
  -webkit-background-clip: text, padding-box, padding-box;
          background-clip: text, padding-box, padding-box;
  --_t: calc(var(--_i,0)*.5s);
  transition: 
    var(--_t),
    color calc(.5s - var(--_t)) var(--_t),
    background-color calc(.5s - var(--_t)) var(--_t);
}
.hover:hover {
  --_i: 1;
}

如果您想知道为什么我选择了主要颜色的 RGB 语法,那是因为我需要使用 alpha 透明度。 我还使用变量 --_t 来减少 transition 属性中使用的冗余计算。

在我们进入下一部分之前,这里有一些我之前做的依赖 background-clip 的悬停效果示例。 对每个示例进行详细说明会太长,但通过我们目前所学到的知识,您可以轻松理解代码。 它可以成为您独自尝试一些内容的好灵感,而无需查看代码。

我知道,我知道。 这些是疯狂且不常见的悬停效果,我意识到在大多数情况下它们过于复杂。 但这就是练习和学习 CSS 的方法。 请记住,我们正在挑战 CSS 悬停效果的极限。 悬停效果可能只是新奇事物,但我们在此过程中正在学习新的技术,这些技术当然可以用于其他方面。

使用 CSS mask 的悬停效果

猜猜看? CSS mask 属性使用渐变的方式与 background 属性相同,因此您会发现接下来我们要做的非常简单。

让我们从制作一个花哨的下划线开始。

我在该示例中使用 background 创建一个锯齿形底部边框。 如果我想对该下划线应用动画,仅使用背景属性进行操作会很繁琐。

CSS mask 登场。

代码可能看起来很奇怪,但逻辑与我们之前所有背景动画的逻辑相同。 mask 由两个渐变组成。 第一个渐变使用不透明颜色定义,覆盖内容区域 (由于 content-box 值)。 那个第一个渐变使文本可见,并隐藏底部锯齿形边框。 content-boxmask-clip 值,其行为与 background-clip 相同

linear-gradient(#000 0 0) content-box

第二个渐变将覆盖整个区域 (由于 padding-box)。 它具有使用 --_p 变量定义的宽度,并且将放置在元素的左侧。

linear-gradient(#000 0 0) 0 / var(--_p, 0%) padding-box

现在,我们所要做的就是悬停时更改 --_p 的值,为第二个渐变创建滑动效果并显示下划线。

.hover:hover {
  --_p: 100%;
  color: var(--c);
}

下面的示例使用蒙版层作为背景,以便更好地了解正在发生的技巧。 想象一下,绿色和红色部分是元素的可见部分,而其他所有内容都是透明的。 如果我们将相同的渐变与之一起使用,蒙版就会这样做。

通过这样的技巧,我们可以通过简单地使用 mask 属性的不同渐变配置,轻松创建很多变化

该示例中的每个示例都对 mask 使用了略微不同的渐变配置。 另外请注意代码中背景配置和蒙版配置之间的分离。 它们可以独立管理和维护。

让我们更改背景配置,用波浪形下划线替换锯齿形下划线

又一组悬停效果! 我保留了所有蒙版配置并更改了背景以创建不同的形状。 现在,您了解了为什么我可以 实现 400 种悬停效果 而无需伪元素——而且我们还可以有更多!

例如,为什么不试试这个

这里有一个挑战给您: 上一个示例中的边框是一个使用 mask 属性的渐变,用来显示它。 您能弄明白动画背后的逻辑吗? 乍一看可能很复杂,但它与我们为大多数其他依赖渐变的悬停效果所看到的逻辑非常相似。 在评论区中发布您的解释!

3D 悬停效果

你可能认为用单个元素(并且不使用伪元素!)创建 3D 效果是不可能的,但 CSS 有一种方法可以实现。

你所看到的不是真正的 3D 效果,而是在 2D 空间中结合了 CSS backgroundclip-pathtransform 属性的完美 3D 错觉。

Breakdown of the CSS hover effect in four stages.
这个技巧看起来像是我们正在与 3D 元素交互,但实际上我们只是使用 2D 技巧来绘制一个 3D 盒子。

我们首先定义变量。

--c: #1095c1; /* color */
--b: .1em; /* border length */
--d: 20px; /* cube depth */

然后我们使用上面的变量创建具有宽度的透明边框。

--_s: calc(var(--d) + var(--b));
color: var(--c);
border: solid #0000; /* fourth value sets the color's alpha */
border-width: var(--b) var(--b) var(--_s) var(--_s);

元素的顶部和右侧都需要等于 --b 值,而底部和左侧需要等于 --b--d 的总和(即 --_s 变量)。

为了实现这个技巧的第二部分,我们需要定义一个覆盖我们之前定义的所有边框区域的渐变。一个 conic-gradient 可以做到这一点。

background: conic-gradient(
  at left var(--_s) bottom var(--_s),
  #0000 90deg,var(--c) 0
 ) 
 0 100% / calc(100% - var(--b)) calc(100% - var(--b)) border-box;
Diagram of the sizing used for the hover effect.

对于这个技巧的第三部分,我们添加另一个渐变。这个渐变将使用两个半透明的白色颜色值,它们会与前面的第一个渐变重叠,以创建主颜色的不同色调,从而使我们产生阴影和深度的错觉。

conic-gradient(
  at left var(--d) bottom var(--d),
  #0000 90deg,
  rgb(255 255 255 / 0.3) 0 225deg,
  rgb(255 255 255 / 0.6) 0
) border-box
Showing the angles used to create the hover effect.

最后一步是应用 CSS clip-path 来剪切角落,以获得那种长阴影的感觉。

clip-path: polygon(
  0% var(--d), 
  var(--d) 0%, 
  100% 0%, 
  100% calc(100% - var(--d)), 
  calc(100% - var(--d)) 100%, 
  0% 100%
)
Showing the coordinate points of the three-dimensional cube used in the CSS hover effect.

就是这样!我们只用两个渐变和一个 clip-path 就创建了一个 3D 矩形,我们可以使用 CSS 变量轻松地调整它。现在,我们只需要对其进行动画处理!

注意上图中(以红色标出)的坐标。让我们更新它们来创建动画。

clip-path: polygon(
  0% var(--d), /* reverses var(--d) 0% */
  var(--d) 0%, 
  100% 0%, 
  100% calc(100% - var(--d)), 
  calc(100% - var(--d)) 100%, /* reverses 100% calc(100% - var(--d)) */ 
  0% 100% /* reverses var(--d) calc(100% - var(--d)) */
)

这个技巧是隐藏元素的底部和左侧部分,这样剩下的就是一个完全没有深度的矩形元素。

这个例子将动画的 clip-path 部分隔离,以便查看它是如何工作的。

最后的润色是用 translate 将元素向相反方向移动——这样错觉就完美了!以下是使用不同的自定义属性值来实现不同深度的效果。

第二个悬停效果遵循相同的结构。我所做的只是更新了一些值,以创建一个左上角的移动而不是右上角的移动。

组合效果!

我们所介绍的所有内容的妙处在于它们都是相辅相成的。以下是一个例子,我在其中添加了 第二篇文章中的 text-shadow 效果第一篇文章中的 background 动画技巧,同时使用我们刚刚介绍的 3D 技巧。

代码本身可能一开始看起来很混乱,但你可以进一步分析它——你会注意到它只是将这三种不同的效果组合在一起,基本上是将它们揉合在一起。

让我们用最后一个悬停效果来结束这篇文章,在其中我将 backgroundclip-path 和一点 perspective 结合起来,模拟另一个 3D 效果。

我将相同的效果应用于图像,结果非常好,可以模拟单个元素的 3D 效果。

想要更详细地了解最后一个演示是如何工作的?我写了一篇关于它的文章。

总结

哇,我们完成了!我知道,很多 CSS 技巧都比较难,但 (1) 我们在正确的地方, (2) 目的是通过让不同的 CSS 属性相互交互,来提高我们对它们的理解。

你现在可能会问,既然我们已经完成了这个关于高级 CSS 悬停效果的小系列,下一步该做什么。我认为下一步是用我们学到的所有知识来处理其他元素,比如按钮、菜单项、链接等等。为了达到这个目的,我们把事情做得很简单,只将我们的技巧限制在标题元素上;实际的元素并不重要。掌握这些概念并运用它们来创建、实验和学习新事物!