如何使用边框图像动画化 SVG

Avatar of Uri Shaked
Uri Shaked

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

让我们看看如何将 CSS 中的 border-image 属性与围绕边框移动的 SVG 动画结合起来。 在此过程中,我们将介绍如何手工制作可调整大小的九宫格动画 SVG,您不仅可以使用它来重新创建效果,还可以根据自己的需要进行定制。

这是我们正在制作的内容

Animated gif of red skulls moving around a list of high scores in a retro arcade font that go from first place to tenth place.
恐怖骷髅?复古街机?有什么不喜欢的?!

这实际上是我正在进行的一个夺旗谜题 The Skull 的一部分,旨在探索 Arduino 及其微控制器的内部结构。 我搜索了如何像这样为边框设置动画,但找不到任何有用的示例。 我找到的大多数内容都与 蚂蚁行进 有关,但不幸的是,stroke-dasharray 技巧不适用于骷髅,更不用说更复杂的形状了。

因此,本着学习和分享的精神,我在这里与您一起撰写这篇博文!

我们应该使用 background 还是 border-image

起初,我甚至不知道 border-image 是什么。 在我的第一次尝试中,我尝试使用 ::before 伪元素,并为其 background-position 属性设置动画。 这让我走到了这一步

如您所见,它确实有效,但要完成边框,至少需要八个不同的元素(或伪元素)。 这样杂乱 HTML 不是理想的做法。

我在以色列 CSS 开发人员 Facebook 群组中发布了一个问题,每个人都向我指出了 border-image 属性。 它完全按照其字面意思执行:使用图像(或 CSS 渐变)作为元素的边框。

要使用 border-image,您必须提供一个以九宫格方式使用的图像(想想图像上的井字棋棋盘)。 这九个区域中的每一个都代表边框的不同部分:顶部、右侧、左侧和底部,四个角中的每一个,以及中间(被忽略)。

例如,如果我们只需要静态骷髅,我们可以 利用 SVG 模式 将骷髅重复九次。 首先,我们使用骷髅的路径定义一个 24×24 的模式,然后使用此模式作为 72×72 矩形的 fill

<svg version="1.1" height="72" width="72" xmlns="http://www.w3.org/2000/svg">
 <defs>
  <pattern id="skull-fill" width="24" height="24" 
patternUnits="userSpaceOnUse">
    <path d="..." fill="red"/>
  </pattern>
 </defs>
 <rect fill="url(#skull-fill)" width="72" height="72" />
</svg>

接下来,我们定义一个边框并在目标元素上设置 border-image

.skulls {
  border: 24px solid transparent;
  border-image: url("https://skullctf.com/images/skull-9.svg") 24 round;
}

我们得到一个由骷髅组成的边框

添加 SVG 动画

现在我们可以为这些骷髅设置动画了! 它有效,嗯,在大多数情况下。

我们的想法是为边框图像中的每个区域创建不同的动画。 例如,在左上角,我们有一个骷髅从右到左移动,而第二个骷髅同时从上到下移动。

我们将为运动设置 transform 属性的动画。 我们还将 利用 SVG 的 <use> 来避免为每个骷髅重复冗长的 <path> 定义

<svg version="1.1" height="96" width="96" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <style>
  @keyframes left {to {transform: translate(-32px, 0)}}
  @keyframes down {to {transform: translate(0, 32px)}}
 </style>
 <defs>
  <path id="skull" d="..." fill="red"/>
 </defs>

 <!-- Top-left corner: one skull goes left, another goes down -->
 <use href="#skull" x="0" y="0"  style="animation: down .4s infinite linear"/>
 <use href="#skull" x="32" y="0" style="animation: left .4s infinite linear"/>
</svg>

那里的 SVG 动画语法可能看起来很熟悉,因为与其使用某种 SVG 特定的语法(如 SMIL),它只是使用 CSS 动画。 很酷,对吧?

这是我们得到的结果

如果我们添加一个网格,我们可以看到此动画也覆盖了部分顶部和左侧边缘

在添加了其余三个边框后,它开始变得更加令人印象深刻,从而完全覆盖了边框图像的所有八个区域

<svg version="1.1" height="96" width="96" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <style>
  @keyframes left {to {transform: translate(-32px, 0)}}
  @keyframes down {to {transform: translate(0, 32px)}}
  @keyframes right {to {transform: translate(32px, 0)}}
  @keyframes up {to {transform: translate(0, -32px)}}
 </style>
 <defs>
  <path id="skull" d="..." fill="red"/>
 </defs>

 <!-- Top-left corner: one skull goes left, another goes down -->
 <use href="#skull" x="0" y="0"  style="animation: down .4s infinite linear"/>
 <use href="#skull" x="32" y="0" style="animation: left .4s infinite linear"/>

 <!-- Top-right corner: one skull goes up, another goes left -->
 <use href="#skull" x="64" y="0" style="animation: left .4s infinite linear"/>
 <use href="#skull" x="64" y="32" style="animation: up .4s infinite linear"/>

 <!-- Bottom-left corner: one skull goes down, another goes right -->
 <use href="#skull" x="0" y="32" style="animation: down .4s infinite linear"/>
 <use href="#skull" x="0" y="64" style="animation: right .4s infinite linear"/>

 <!-- Bottom-right corner: one skull goes right, another goes up -->
 <use href="#skull" x="32" y="64" style="animation: right .4s infinite linear"/>
 <use href="#skull" x="64" y="64" style="animation: up .4s infinite linear"/>
</svg>

这为我们提供了一个完整的回路

Dancing skulls, ready to go into your border!

将所有内容组合在一起,我们将刚刚创建的动画 SVG 作为 border-image 使用,并获得所需的结果

我可以整天玩这个……

一旦我让它工作起来,我就开始调整动画属性。 这是使用 SVG 而不是 GIF 的优势之一:更改动画的本质就像更改 SVG 源文件中的一个 CSS 属性一样简单,并且您可以立即看到结果,更不用说更小的文件大小(尤其是在处理渐变时)、完整的颜色支持和清晰的缩放。

首先,我尝试查看如果我将动画时间函数更改为 ease 会是什么样子

我们还可以使骷髅在红色和绿色之间淡入淡出

我们甚至可以使骷髅在绕着高分表移动时改变方向

转到 JavaScript 选项卡,您可以在其中调整 SVG 源代码并亲自尝试。

房间里的大象 🐘(咳咳,Firefox)

当我第一次让它工作时,我非常高兴。 但是,您应该了解一些注意事项。 首先也是最重要的是,由于某种原因,Firefox 不会渲染边框边缘的动画,而只渲染角上的动画

有趣的是,如果我将 SVG 更改为具有相同动画的 GIF,它可以完美运行。 但随后 Chrome 上的边框停止动画!🤦‍♂️

无论如何,这似乎是一个浏览器错误,因为如果我们将 border-image-repeat 属性更改为 stretch,Firefox 会为边框设置动画,但结果有点古怪(尽管它可能适合页面的主题)

border-image-repeat 值更改为 space 似乎也有效,但前提是元素的宽度不是骷髅大小的整数倍,这意味着动画中会有一些间隙。

我还发现了一些视觉问题,例如当容器大小不是补丁大小(在本例中为 32px)的倍数时,例如骷髅上的细黑线。 我怀疑这与某些浮点数舍入问题有关。 在放大时,它也容易出现问题。


并不完美,但肯定完成了! 如果您想查看最终版本的实际效果,欢迎您查看 The Skull 的高分 页面。 希望很快就会有您的名字出现在上面!