CSS Tips:边框动画

在 Web 设计中,边框动画是为页面增添活力和吸引力的重要元素之一。然而,对于许多 Web 开发者来说,使用 CSS 制作边框动画并不容易。除了简单的过渡效果,更复杂的动画往往需要创造性的解决方案。今天,我将向你展示如何通过 CSS 制作各种炫酷的边框动画,不仅仅限于实际的边框属性,还包括利用"虚拟"边框、轮廓甚至 SVG 元素。希望你能在实际生产中利用这些有用的技巧和方法,创造出令人惊叹的边框动画!

你需要具备些什么?

如果你希望能快速的掌握接下来分享的"小技巧",并且利用这些小技巧创造出有创意,吸引人的边框动画。我觉得你可能需要具备以下一些基础:

  • 制作边框的方案:除了你熟悉的 borderoutlinebox-shadow 可以设置元素边框之外,还可以使用 CSS 伪元素 ::before::after ,甚至是 SVG 技术给元素设置边框

  • 了解伪元素和伪类:你可能需要使用伪元素(如 ::before::after)或伪类(如 :hover)来实现特定的边框效果

  • CSS 动画:熟悉 CSS 过渡(transition)和关键帧动画@keyframesanimation)。它们可以让你创建平滑的动画效果,包括边框动画

  • 熟悉 CSS 动画技巧:学习一些 CSS 动画的技巧和最佳实践,例如使用关键帧动画、缓动函数等来增强你的边框动画效果

  • SVG 图形:有时候,你可能需要使用 SVG 图形来创建更复杂的边框动画

  • 调试工具:学会使用浏览器的开发者工具来调试和优化你的边框动画。

  • 创造力和实践:最后但同样重要的是,发挥自己的创造力,实践不同的边框动画效果。通过不断尝试和实验,你将能够提高你的技能,并创造出更令人印象深刻的动画效果

除此之外,接下来你可能会应用到的一些 CSS 特性。例如背景渐变路径动画滚动驱动动效变换裁剪、遮罩自定义属性以及 @property 等。

虚拟边框

不使用 CSS border 的主要原因是出于动画目的

CSS 盒模型中的 border 属性存在意义就是给元素设置边框的。但很多时候,border 属性无法满足更多场景下下的边框设置。因此,很多优秀的 Web 开发者发挥自己的聪明才智,使用 CSS 的其他属性(例如,outlinebox-shadow 等)给元素设置边框效果,往往效果还要比 border 好。因为它们不受模模型的限制,在制作一些悬浮效果时,不会出现抖动的效果。

虽然这些方法(borderoutlinebox-shadow )都可以用于给元素设置边框,但每种方法都有自己的优缺点,尤其是在为边框添加动画效果时。比如,border 只适用一些简单的颜色过渡效果,box-shadow 虽可以实现一些复杂的效果,但耗性能。因此,我们需要一种更好的方式来替代它们,使我们能更好的为元素添加带有动画效果的边框。这种技术,常称之为"虚拟边框"技术!

简单地说,虚拟边框指的是在 CSS 中使用其他技术来模拟边框的效果,而不是直接使用实际的 CSS border 属性。例如通过内距 padding 和背景来模拟边框、使用伪元素 ::before::after 来充当边框以及使用 SVG 元素来当作边框。这种技术通常用于创建更复杂的边框动画或样式,例如使用伪元素和动画来模拟边框的出现、消失或变化过程,而不受实际 CSS 边框属性(border)的限制。虚拟边框技术可以提供更大的灵活性和创造性,使 Web 开发者能够实现各种视觉效果而不受传统边框样式的限制。

接下来,我们来看几种常见的虚拟边框的实现方案。

盒模型特性 + 背景图片

简单地说,利用 CSS 盒模型的特性和背景特性可以快速模拟出边框的效果。这种技术你需要对 CSS 盒模型和 CSS 背景特性要有足够的了解,尤其是 CSS 的 background-clipbackground-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-originbackground-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-* 属性,你可以沿着其按钮周长对元素进行动画处理

  • insettoprightbottomleft 的简写属性

  • @keyframes 动画仅仅是将该伪元素的 offset-distance 动画到 100%

  • offset-anchor 属性允许你指定元素的哪一点沿着路径移动

  • 剪切掉所有多余的部分,以便只保留边框,并仍然可以具有半透明的背景等

  • 使用带有 mask-compositemask

简单地说,在这个边框动画效果中,应用到了 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 相关的知识,请关注我的专栏,或者移步阅读下面这些系列教程:

相关推荐
时清云23 分钟前
【算法】合并两个有序链表
前端·算法·面试
小爱丨同学31 分钟前
宏队列和微队列
前端·javascript
持久的棒棒君1 小时前
ElementUI 2.x 输入框回车后在调用接口进行远程搜索功能
前端·javascript·elementui
2401_857297911 小时前
秋招内推2025-招联金融
java·前端·算法·金融·求职招聘
undefined&&懒洋洋2 小时前
Web和UE5像素流送、通信教程
前端·ue5
大前端爱好者4 小时前
React 19 新特性详解
前端
小程xy4 小时前
react 知识点汇总(非常全面)
前端·javascript·react.js
随云6324 小时前
WebGL编程指南之着色器语言GLSL ES(入门GLSL ES这篇就够了)
前端·webgl
随云6324 小时前
WebGL编程指南之进入三维世界
前端·webgl
寻找09之夏5 小时前
【Vue3实战】:用导航守卫拦截未保存的编辑,提升用户体验
前端·vue.js