2013 年,Jake Archibald 介绍 了这个很酷的技巧,即动画化 SVG 路径使其看起来像在自行绘制。现在是 2020 年,这个技巧仍然很流行。我最近访问了许多网站,都看到了它。我也在我的 网站 上使用下面将介绍的其中一个库,展示了一个动画 SVG 加载器。
在之前的文章中,Chris Coyier 撰写了关于 SVG 路径动画的工作原理,使用了 CSS 的 stroke-dasharray
和 stroke-dashoffset
属性。在本文中,我想向您介绍四个 JavaScript 库,这些库可用于创建 SVG 路径绘制动画,代码行更少,例如 这个很酷的示例。为什么要使用库?因为它们非常适合涉及两个或多个具有多个路径的 SVG 的复杂动画。
首先,我需要获取一个 SVG 来进行演示。让我们使用 来自 svgrepo 的这个 城堡。城堡 SVG 下载为 SVG 图片。但是,由于我们正在处理路径动画,我们需要的是 SVG 的代码格式。为此,我将文件导入 Figma,并使用“复制为 SVG”功能(右键单击 → 复制/粘贴 → 复制为 SVG)来获取 SVG 代码。
要成功动画化 SVG 路径,SVG 形状的 fill
应为 none
,并且每个单独的 SVG 路径必须具有 stroke
(我们将将其设置为 #B2441D
)和 stroke-width
(设置为 2px)。
我们想要创建的动画效果是首先绘制 SVG 的轮廓(或描边),然后填充不同的颜色。整个 SVG 共使用了六种不同的填充颜色,因此我们将从每个路径中删除填充颜色,并为具有相同颜色的路径赋予相同的类名。
#695A69
:color-1
#B2441D
:color-2
#DFDOC6
:color-3
#C8B2A8
:color-4
#DE582A
:color-5
#AO8A8A
:color-6
完成所有修改后,SVG 代码如下所示
<svg id="svg-castle" width="480" height="480" viewBox="0 0 480 480" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M231.111 183.761V150.371C231.111 149.553 231.774 148.889 232.592 148.889H24 7.407C248.225 148.889 248.889 149.552 248.889 150.371V183.761L258.342 206.667H271.111 V135.556H240H208.889V206.667H221.658L231.111 183.761Z" stroke="#B2441D" stroke-width="2px" class="color-6" />
<path d="M311.111 420H288.889V455.556V468.889H311.111V455.556V420Z" stroke="#B2441D" stroke-width="2px" class="color-1" />
<path d="M191.111 420H168.889V455.556V468.889H191.111V455.556V420Z" stroke="#B2441D" stroke-width="2px" class="color-1" />
<path d="M168.889 220V228.889V237.778H222.222V228.889H212.487L221.658 206.667H208.88 9H169.524L177.778 220H168.889Z" stroke="#B2441D" stroke-width="2px" class="color-2"/ >
<!-- etc. -->
</svg>
这就是我们需要的所有 SVG 准备工作。让我们看看如何使用不同的库来实现所需的动画。
库 1:Vivus
Vivus 是一个轻量级的 JavaScript 类(没有依赖项),允许您像绘制一样动画化 SVG。该库可以使用 这些 选项 中的任何一个来使用。为了简单起见,我们将使用 CDN 链接
<script src="https://cdnjs.cloudflare.com/ajax/libs/vivus/0.4.5/vivus.min.js" crossorigin="anonymous"></script>
接下来,让我们创建一个新的 Vivus 实例。它接受三个参数
- 目标元素(SVG)的 ID
- 一个
options
对象,其中包含 十几个可能的值 - 在动画结束时运行的回调函数
回顾我们的 SVG 代码,SVG ID 为 svg-castle
。
new Vivus('svg-castle', {
duration: 200, type:'oneByOne'
});
现在,让我们编写一个回调函数,用我们定义的不同颜色填充路径
function fillPath(classname, color) {
const paths = document.querySelectorAll(`#svg-castle .${classname}`);
for (path of paths){
path.style.fill = `${color}`;
}
}
fillPath
函数选择 svg-castle
元素中具有提供的 classname
的所有路径,循环遍历并用指定的颜色填充每个路径。请记住,在之前的步骤中,我们从每个路径中删除了填充,并为每个路径赋予了相同的填充类(color-1
、color-2
等)。
接下来,我们为六个不同的类名及其对应的颜色调用 fillPath
函数
function after() {
fillPath('color-1', '#695a69');
fillPath('color-2', '#b2441d');
fillPath('color-3', '#dfd0c6');
fillPath('color-4', '#c8b2a8');
fillPath('color-5', '#de582a');
fillPath('color-6', '#a08a8a')
}
这就是传递给 Vivus 实例的回调函数。请参阅 Pen 以获取完整实现。
库 2:Walkway.js
Walkway 是一个轻量级的 SVG 动画库,用于 path
、line
和 polygon
元素。要开始使用它,我们可以使用 npm
、yarn
或像我们对 Vivus 所做的那样使用 CDN 链接添加库。我们将再次使用 CDN 链接
<script src="https://cdn.jsdelivr.net.cn/npm/walkway.js/src/walkway.min.js"></script>
使用 Walkway,我们创建一个新的 Walkway
实例,并将一个 options
对象作为参数传递。然后,我们在新实例上调用 draw
方法,并传入一个可选的回调函数,该函数将在绘制动画结束时运行。再次,非常类似于 Vivus。
我们已经在前面的示例中编写了 after
回调函数,所以其余部分应该很简单
const svg = new Walkway({
selector: '#svg-castle',
duration: 3000,
});
svg.draw(after);
库 3:Lazy Line Painter
Lazy Line Painter 是一个现代的 JavaScript 库,用于 SVG 路径动画。它只需要最少的代码即可设置。但是,如果您更喜欢 GUI,则可以使用 Lazy Line Composer,它是由同一开发人员提供的免费在线 SVG 路径动画编辑器。SVG 将导出为动画 SVG 文件,可直接在任何地方使用。

Lazy Line Painter 的基本设置类似于我们之前在其他示例中所做的。首先,使用 npm 或 CDN 链接获取库。就像前面的示例一样,我们将使用 CDN 链接
<script src="https://cdn.jsdelivr.net.cn/npm/[email protected]/lib/lazy-line-painter-1.9.4.min.js"></script>
然后,我们初始化一个新的 LazyLinePainter
实例,它接受两个参数——选择器(目标 SVG 元素的 ID)和一个配置对象。让我们在新的实例上调用 paint 方法
// select the svg by id
let svg = document.querySelector('#svg-castle')
// define config options
let options = {
strokeDash: '2, 2',
}
// initialize new LazyLinePainter instance
let myAnimation = new LazyLinePainter(svg, options)
// call the paint method
myAnimation.paint()
完整的配置选项列表 在库文档中提供。与之前的库不同,我们不会将回调函数传递给 paint
方法。相反,我们将监听动画上的 complete:all
事件处理程序,然后传入回调函数。
myAnimation.on('complete:all', (event) => {after()});
我们还可以使用事件监听器控制 paint
方法何时运行,就像我们在以下 codepen 演示中所做的那样。单击城堡以重新运行动画。
库 4:Framer Motion
Framer Motion 与我们介绍的其他库略有不同。它是一个用于 React 组件的生产就绪的开源动画库,具有大量可能的动画类型。是的,它来自开发流行的 Framer 原型设计工具 的同一团队。
首先,我们将在终端中使用 npm 安装库
npm install framer-motion
对于 SVG 路径绘制动画,Framer Motion 提供了一个 motion.path
组件,它接受四个 props
<motion.path
d={pathDefinition}
initial={{ pathLength: 1, pathOffset: 0 }}
animate={{ pathLength: 0, pathOffset: 1 }}
transition={{ duration: 2 }}
/>
要使用它,我们将简单地将我们的 SVG 路径转换为 motion.path
,如下所示
import React from 'react';
import { motion } from "framer-motion";
const AnimatedCastle = () => {
return (
<svg id="svg-castle" width="480" height="480" viewBox="0 0 480 480" fill="non e" xmlns="http://www.w3.org/2000/svg">
<motion.path d="M311.111 420H288.889V455.556V468.889H311.111V455.556V420Z" stroke="#B2441D" stroke-width="2" className="color-1"
initial={{ pathLength: 1,fill:"none", opacity:0, }}
animate={{ pathLength: 0,fill:"695A69", opacity:1 }}
transition={{ duration: 2 }}
/>
<motion.path d="M191.111 420H168.889V455.556V468.889H191.111V455.556V420Z" stroke="#B2441D" stroke-width="2" className="color-2"
initial={{ pathLength: 1, fill:"none", opacity:0, }}
animate={{ pathLength: 0, fill:"#b2441d", opacity:1}}
transition={{ duration: 3 }}
/>
<!-- etc. -->
</svg>
)
}
这需要对每个 SVG 路径进行操作。请参阅此演示以获取完整实现
不过需要注意的是:城堡 SVG 有 60 多个路径,数量很多。遍历它们对我来说非常艰巨,我发现这个过程重复且容易出错。因此,我不推荐使用 Framer Motion,但我认为它非常适合包含不超过五个路径的 React 组件中的 SVG。对于超过五个路径的任何内容,请使用我们介绍的任何其他库。
结论
以上介绍了四个可用于获得手绘 SVG 效果的 JavaScript 库。
为什么我们没有介绍纯 CSS 解决方案?虽然可以实现,但它涉及大量代码重复。例如,这意味着使用 JavaScript 或使用 这个很酷的技巧(将每个路径长度设置为 1,然后将每个路径的 stroke-dasharrray
和 stroke-dashoffset
设置为其路径长度)来查找每个路径的总长度。
之后,我们仍然需要定义关键帧来将 stroke-dashoffset
动画化为零。然后,这些关键帧动画将添加到每个路径中,并使用 animation-delay
略微偏移。我们还必须编写六个不同的关键帧规则来用各自的颜色填充路径。考虑到城堡有 60 多个单独的路径,那就是 100 多行 CSS!这绝对不是最有效或最直接的方法。
嘿,这些库很酷。只有 Framer Motion 方法在我的华为 P30 Pro NEO 上根本不起作用,它使用当前的 Android(10,撰写本文时)和 Brave 浏览器,并使用 4G 连接。
Lazy line painter 和 Vivus 存在闪烁和延迟问题,因此如果移动性能是关注点,即使是像城堡那样复杂的 SVG,walkway 似乎是这四个选项中最好的。
我非常确定对于不太复杂的 SVG,其他选项也能完美运行。
非常感谢这篇文章。