在 Web 设计中,边框动画是为页面增添活力和吸引力的重要元素之一。然而,对于许多 Web 开发者来说,使用 CSS 制作边框动画并不容易。除了简单的过渡效果,更复杂的动画往往需要创造性的解决方案。今天,我将向你展示如何通过 CSS 制作各种炫酷的边框动画,不仅仅限于实际的边框属性,还包括利用"虚拟"边框、轮廓甚至 SVG 元素。希望你能在实际生产中利用这些有用的技巧和方法,创造出令人惊叹的边框动画!
你需要具备些什么?
如果你希望能快速的掌握接下来分享的"小技巧",并且利用这些小技巧创造出有创意,吸引人的边框动画。我觉得你可能需要具备以下一些基础:
-
制作边框的方案:除了你熟悉的
border
、outline
和box-shadow
可以设置元素边框之外,还可以使用 CSS 伪元素::before
或::after
,甚至是 SVG 技术给元素设置边框 -
了解伪元素和伪类:你可能需要使用伪元素(如
::before
和::after
)或伪类(如:hover
)来实现特定的边框效果 -
CSS 动画:熟悉 CSS 过渡(transition)和关键帧动画(
@keyframes
和animation
)。它们可以让你创建平滑的动画效果,包括边框动画 -
熟悉 CSS 动画技巧:学习一些 CSS 动画的技巧和最佳实践,例如使用关键帧动画、缓动函数等来增强你的边框动画效果
-
SVG 图形:有时候,你可能需要使用 SVG 图形来创建更复杂的边框动画。
-
调试工具:学会使用浏览器的开发者工具来调试和优化你的边框动画。
-
创造力和实践:最后但同样重要的是,发挥自己的创造力,实践不同的边框动画效果。通过不断尝试和实验,你将能够提高你的技能,并创造出更令人印象深刻的动画效果
除此之外,接下来你可能会应用到的一些 CSS 特性。例如背景、渐变、路径动画、滚动驱动动效、变换、裁剪、遮罩和自定义属性以及 @property
等。
虚拟边框
不使用 CSS
border
的主要原因是出于动画目的!
CSS 盒模型中的 border 属性存在意义就是给元素设置边框的。但很多时候,border
属性无法满足更多场景下下的边框设置。因此,很多优秀的 Web 开发者发挥自己的聪明才智,使用 CSS 的其他属性(例如,outline
和 box-shadow
等)给元素设置边框效果,往往效果还要比 border
好。因为它们不受模模型的限制,在制作一些悬浮效果时,不会出现抖动的效果。
虽然这些方法(border
、outline
和 box-shadow
)都可以用于给元素设置边框,但每种方法都有自己的优缺点,尤其是在为边框添加动画效果时。比如,border
只适用一些简单的颜色过渡效果,box-shadow
虽可以实现一些复杂的效果,但耗性能。因此,我们需要一种更好的方式来替代它们,使我们能更好的为元素添加带有动画效果的边框。这种技术,常称之为"虚拟边框"技术!
简单地说,虚拟边框指的是在 CSS 中使用其他技术来模拟边框的效果,而不是直接使用实际的 CSS border
属性。例如通过内距 padding
和背景来模拟边框、使用伪元素 ::before
或 ::after
来充当边框以及使用 SVG 元素来当作边框。这种技术通常用于创建更复杂的边框动画或样式,例如使用伪元素和动画来模拟边框的出现、消失或变化过程,而不受实际 CSS 边框属性(border
)的限制。虚拟边框技术可以提供更大的灵活性和创造性,使 Web 开发者能够实现各种视觉效果而不受传统边框样式的限制。
接下来,我们来看几种常见的虚拟边框的实现方案。
盒模型特性 + 背景图片
简单地说,利用 CSS 盒模型的特性和背景特性可以快速模拟出边框的效果。这种技术你需要对 CSS 盒模型和 CSS 背景特性要有足够的了解,尤其是 CSS 的 background-clip
、background-origin
。它们的值与 CSS 的盒模型刚好匹配。
我将通过下面两个示例来向大家演示,如何利用 CSS 的盒模型与背景图片快速模拟边框。
HTML
<div class="ele"></div>
CSS
.ele {
width: 60vh;
aspect-ratio: 21 / 9;
border-radius: .5em;
}
先来看 border
加渐变的方式。首先给元素设置一个透明的边框,同时给元素设置一个渐变颜色:
CSS
.ele {
border: 2vh solid transparent;
background: linear-gradient(45deg, orange, red) no-repeat center / cover;
}
此时,你并看不到任何边框效果,只能看到元素有一个渐变效果。实际上它已有一个 2vh
透明实线边框存在。
接下来这几行代码才是关键:
CSS
.ele {
background-image: linear-gradient(#fff, #fff), linear-gradient(45deg, orange, red);
background-origin:padding-box, border-box;
background-clip:padding-box, border-box;
}
这里应用了 CSS 多背景的特性,并且通过 background-origin
来调整它的原点位置和使用 background-clip
控制背景图片的裁剪区域。这个时候你将看到一个带有渐变效果的边框:
Demo 地址:codepen.io/airen/full/...
如果你不喜欢渐变颜色边框,只需要一个纯色,那么你可以使用下面的方式来设置一个纯色的渐变效果:
CSS
.gradient {
background-image: linear-gradient(red, red);
/* 或者 👇 */
background-image: linear-gradient(red 0, red 100%);
/* 或者 👇 */
background-image: linear-gradient(red 0 100%);
/* 或者 👇 */
background-image: linear-gradient(red 0 0);
}
你也可以使用 border-image
来替代 background
。
CSS
.ele {
border-radius: .5em;
border: 2vh solid;
border-image: linear-gradient(45deg, orange, red) 1;
}
Demo 地址:codepen.io/airen/full/...
这种方案有一个致命的缺点,border-radius
将失效!
你还可以使用 padding
来替代 border
,但前提条件是,元素不需要使用 padding
:
CSS
.ele {
padding: 2vh; /* 这个是必需的 */
background: linear-gradient(45deg, orange, red) no-repeat center / cover;
background-image: linear-gradient(#fff, #fff), linear-gradient(45deg, orange, red);
background-origin:content-box, padding-box;
background-clip:content-box, padding-box;
}
代码中的 padding
与上一个示例中的 border
是相似的作用,仅仅是一个占位符。与此同时,background-origin
和 background-clip
的值也需要做出相应的调整。
你最终看到的效果是相似的:
Demo 地址:codepen.io/airen/full/...
基于这两种方案,你只需调整渐变效果,就能轻易得到具有独特创意的渐变边框效果:
Demo 地址:codepen.io/airen/full/...
它们是可以真实用于实际生产的,例如下面这个卡片的效果:
Demo 地址:codepen.io/airen/full/...
看不上去错,但有两点需要知道。第一,要注意背景的堆叠顺序:
使用额外的元素
使用额外的元素来模拟边框也是一种常见的技术,你可以通过 CSS 的伪元素 ::before
或(和) ::after
来模拟边框:
CSS
.ele {
--border-width: 2vh;
--radius: 1rem;
--bg-color: #fff;
position: relative;
border: var(--border-width) solid transparent;
border-radius: var(--radius);
background-color: var(--bg-color);
background-clip: padding-box;
&::before {
content: " ";
position: absolute;
inset: calc(var(--border-width) * -1);
z-index: -1;
border-radius: inherit;
background-image: linear-gradient(45deg, orange, red);
}
}
Demo 地址:codepen.io/airen/full/...
使用伪元素的时候,还可以结合 CSS 的遮罩功能一起使用:
CSS
.ele {
--border-width: 2vh;
--radius: 1rem;
position: relative;
border-radius: var(--radius);
border: var(--border-width) solid transparent;
&::before {
content: " ";
position: absolute;
inset: calc(var(--border-width) * -1);
z-index: -1;
border: inherit;
border-radius: inherit;
background-image: linear-gradient(45deg, orange, red);
background-origin: border-box;
mask: linear-gradient(black, black), linear-gradient(black, black);
mask-clip: content-box, border-box;
mask-composite: exclude;
}
}
Demo 地址:codepen.io/airen/full/...
额外的元素还可以使用 SVG:
HTML
<div class="ele">
<svg height="100%" width="100%" xmlns="http://www.w3.org/2000/svg">
<rect rx="8" ry="8" class="line" height="100%" width="100%" stroke-linejoin="round" />
</svg>
</div>
CSS
.ele {
--border-width: 2vh;
--radius: 1rem;
--bg-color: #fff;
position: relative;
border-radius: var(--radius);
svg {
position: absolute;
z-index: -1;
border-radius: var(--radius);
rect {
fill: none;
rx: 16;
ry: 16;
stroke: red;
stroke-width: var(--border-width);
}
}
}
Demo 地址:codepen.io/airen/full/...
你可以通过下面这个 Demo 查看所有方案的源码:
Demo 地址:codepen.io/airen/full/...
各种方案的利弊这里不做相关的阐述!
给边框添加动画效果
有了上面的基础之后,我们就可以开始探讨如何给边框添加动画效果了。注意,我们这里所谓的边框都是"虚拟边框"!
先从最简单和最直接的方案说起。回过头仔细查看前面的案例,我们给虚拟边框都设置了一个 --gradient
,用于设置边框样式:
CSS
.card {
--gradient: conic-gradient(
#381d6a 80%,
#e0d1ff 88%,
#e0d1ff 92%,
#381d6a 100%
);
}
这里使用的是 conic-gradient() 绘制的锥形渐变
,你可以使用任何你喜欢的渐变的函数。在这个情境之下,我们现在要给边框添加动画效果,其实就是要动画化 --gradient
的值。因此,我们可以先改变一下 --gradient
属性的值,给 conic-gradient()
添加一个起始参数:
CSS
.card {
--angle: 45deg;
--gradient: conic-gradient(
from var(--angle),
#381d6a 80%,
#e0d1ff 88%,
#e0d1ff 92%,
#381d6a 100%
);
}
当你尝试着调整 conic-gradient()
函数中的 --angle
自定义属性的值时,边框样式就会得到改变(实质上渐变效果被调整了):
Demo 地址:codepen.io/airen/full/...
这意味着,我们可以在 transition
对 --angle
进行过渡动画处理,或者在 @keyframes
中改变 --angle
的值,从而实现动画效果。但这有一个前提条件,要对 CSS 自定义属性进行动画化处理,需要用到 @property 规则
,必须先对该属性进行注册,这样才能将 CSS 自定义属性的值从一个字符串转换为 @property
指定的值类型,也只有这样才能进行动画化处理:
CSS
@property --angle {
syntax: "<angle>";
inherits: true;
initial-value: 0turn;
}
@keyframes spin {
to {
--angle: 360deg;
}
}
.card {
animation: spin 2s ease infinite;
&:hover {
animation-play-state: paused;
}
}
Demo 地址:codepen.io/airen/full/...
如果你对动画化 CSS 自定义属性相关的知识感兴趣的话,可以移步阅读下面相关教程:
在此基础上,我们可以做得更好一些。比如,使用现代 CSS 中的颜色函数给元素设置高清颜色,例如 oklab() 或 oklch() 函数
,使颜色变得更细腻,还可以使用相对颜色特性,避免渐变的死亡区:
CSS
@property --angle {
syntax: "<angle>";
inherits: true;
initial-value: 0turn;
}
@keyframes spin {
to {
--angle: 360deg;
}
}
.card {
--purple: oklch(60% 0.37 294.7);
--orange: oklch(60% 0.37 64.65);
--yellow: oklch(60% 0.37 109.08);
--blue: oklch(60% 0.37 237.06);
--angle: 45deg;
--gradient: conic-gradient(
in oklch from var(--angle),
var(--purple),
var(--orange),
var(--yellow),
var(--blue),
var(--purple)
);
}
.card {
animation: spin 2s ease infinite paused;
&:hover {
animation-play-state: running;
}
}
Demo 地址:codepen.io/airen/full/...
再来看一个相对颜色的用法:
CSS
.card {
--angle: 45deg;
--gradient: conic-gradient(
from var(--angle) in oklch longer hue,
var(--purple),
var(--orange),
var(--yellow),
var(--blue),
var(--purple)
);
}
上面代码中的 from var(--angle)
表示conic-gradient
渐变的起始角度,in oklch
这是告诉浏览器,使用 OKLCH 颜色空间来计算渐变颜色,longer hue
解释渐变色中如何计算色调。
Demo 地址:codepen.io/airen/full/...
额外提一点,如果你希望给你的 Web 应用提供更多的色彩和更细腻的颜色,那么你就有必要了解现代 CSS 中有关于颜色相关的特性。如果你感兴趣的话,请移步阅读:
除此之外,还有一种全新的制作动画边框的方式,借助 CSS 的路径动画给边框添加动画效果。
HTML
<button class="el">
<span data-glow="true"></span>
<span class="el__content" contenteditable="true">Button</span>
</button>
CSS
@layer demo {
:root {
--saturation: 100%;
--update: 0.2s;
--intent: 0;
--active: 0;
--hue: 320;
--radius: 100;
--border: 2;
}
.el {
--hue: 349;
--border: 2;
--radius: 50;
--glow: 60;
--anchor: 100;
--speed: 2;
--opacity: 1;
--border-radius: calc(var(--radius, 0) * 1px);
--border-width: calc(var(--border) * 1px);
padding: .5em 2em;
position: relative;
border-radius: var(--border-radius);
color: hsl(0 0% 98%);
font-weight: bold;
font-size: clamp(1.25rem, 3vw + 1.5rem, 2rem);
border: var(--border-width) solid hsl(0 0% 20%);
scale: 1;
min-width: 50vh;
min-height: 44px;
cursor: pointer;
background:
radial-gradient(80% 100% at center 120%,hsl(0 0% 100% / 0.5),transparent),
linear-gradient(hsl(var(--hue, 0) var(--saturation) 50% / calc(var(--intent, 0) * 0.25)),hsl(var(--hue, 0) var(--saturation) 50% / calc(var(--intent, 0) * 0.65))), hsl(0 0% 12%);
box-shadow: 0 1px inset hsl(0 0% 100% / 0.5), 0 -1px inset hsl(0 0% 0% / 1);
transition: background var(--update), scale var(--update), transform 1s;
&:is(:hover, :focus-visible) {
--intent: 1;
scale: 1.1;
}
&:active {
--active: 1;
scale: 0.98;
}
.el__content {
outline-color: hsl(var(--hue) 80% 50% / 0.5);
outline-offset: 0.5rem;
color: hsl(0 0% calc((70 + (var(--intent) * 30)) * 1%));
transition: color var(--update);
}
}
[data-glow] {
position: absolute;
inset: calc(var(--border-width) * -1);
border-radius: var(--border-radius);
border: var(--border-width) solid transparent;
mask: linear-gradient(transparent, transparent),
linear-gradient(white, white);
mask-clip: padding-box, border-box;
mask-composite: intersect;
pointer-events: none;
&::before,
&::after {
content: "";
height: calc(var(--glow) * 1px);
aspect-ratio: 1 / 1;
offset-anchor: calc(var(--anchor) * 1%) 50%;
background:
radial-gradient(circle at right, hsl(0 0% 100% / 0.75),transparent 50%),
radial-gradient(circle at right,hsl(var(--hue) var(--saturation) var(--lightness, 50%) / 1) 50%,transparent);
opacity: var(--opacity);
offset-path: rect(0 100% 100% 0 round var(--border-radius));
offset-path: rect(0 100% 100% 0 round calc(var(--glow) * 1px));
position: absolute;
display: inline-block;
animation: loop calc(var(--speed) * 1s) infinite linear;
}
}
@keyframes loop {
to {
offset-distance: 100%;
}
}
}
Demo 地址:codepen.io/airen/full/... (来源于 @Jhey )
说实话,没有一定功底的前端开发者,看到上面的代码你就会选择退出。心理还有一种恐惧感。在这里我并不打算剖析所有代码,但关键点在这里提一下:
-
使用
offset-*
属性,你可以沿着其按钮周长对元素进行动画处理 -
inset
是top
、right
、bottom
和left
的简写属性 -
@keyframes
动画仅仅是将该伪元素的offset-distance
动画到100%
-
offset-anchor
属性允许你指定元素的哪一点沿着路径移动 -
剪切掉所有多余的部分,以便只保留边框,并仍然可以具有半透明的背景等
-
使用带有
mask-composite
的mask
简单地说,在这个边框动画效果中,应用到了 CSS 的渐变、剪切和遮罩、路径动画等特性。
接下来,就是需要你的创意。例如 @Jhey 在 Codepen 提供的几个案例,基于上面所提到的特性,实现边框添加不同的动画效果:
Demo 地址:codepen.io/jh3y/full/z...
Demo 地址:codepen.io/jh3y/full/o...
Demo 地址:codepen.io/jh3y/full/M...
Demo 地址:codepen.io/jh3y/full/y...
注意,上面这个案例还应用了 CSS 滚动驱动动效特性。
你也可以使用 CSS 的变换来构建一些简单的边框动画效果,例如:
Demo 地址:codepen.io/uiswarup/fu... (来源于 @Swarup Kumar Kuila )
Demo 地址:codepen.io/Metty/full/... (来源于 @Metty )
最后,请不要遗忘了 SVG 的 stroke-dasharray 和 stroke-dashoffset ,它们将得到不同的线条动画:
Demo 地址:codepen.io/airen/full/...
小结
在本文中,我们探讨了在 CSS 中创建虚拟边框和对其进行动画处理的方法。尽管通常首选传统的 CSS 边框属性(border
),但某些设计需求可能需要更复杂的动画效果,这些效果仅靠传统方法无法实现。因此,我们深入探讨了一些高级技术,以满足这些特定的设计需求,为增强 Web 开发项目中的 CSS 边框动画提供了宝贵的见解。
特别声明,课程中应用了很多现代 CSS 特性以及制作 Web 动画相关的知识,如果你想更进一步掌握这方面的知识,请移阅读阅读《现代 CSS》和 《Web 动画之旅》两个系列课程。将会让你对 CSS 有一个全新的认识。
如果你对 CSS 方面的技巧感兴趣,请移步阅读:
如果你觉得该教程对你有所帮助,请给我点个赞。要是你喜欢 CSS ,或者想进一步了解和掌握 CSS 相关的知识,请关注我的专栏,或者移步阅读下面这些系列教程: