自从我还是个孩子的时候,我就对变形效果很感兴趣。 形状转换动画总是能吸引我的注意力。 第一次看到变形让我疑惑“哇,他们是怎么做到的?” 从那时起,我就创建了演示并撰写了关于此效果的文章。
在支持变形的不同动画库方面,有很多选择。 其中许多都很好,并提供了许多功能。 最近,我被react-spring吸引住了。 React-spring 是一个基于 React 构建的、功能强大的物理动画库。 Adam Rackis 最近发布了关于它的不错概述。 该库附带了许多功能,包括(除其他外)SVG 变形。 事实上,react-spring 的魅力在于它如何支持变形。 它允许您直接在定义 SVG 路径描述符的标记中执行此操作。 从簿记的角度来看,这很好。 SVG 路径描述符通常是您期望它们存在的地方。
这是一段展示本文内容的视频
它是在入门序列中的变形效果。 在这里,它用作背景效果。 它旨在补充前景动画; 使其更突出,而不是占据整个场景。
创建 SVG 文档
我们要做的第一件事是创建底层模型。 通常,一旦我对想要做什么有了清晰的认识,我就会创建某种设计。 我的大多数探索都从模型开始,以演示结束。 在大多数情况下,这意味着在我的矢量编辑器中创建 SVG 文档。 我使用Inkscape来绘制我的 SVG。
创建 SVG 文档时,我使用精确的比例。 我发现精确一些更好。 对我来说,当我在矢量编辑器中使用与浏览器和代码编辑器相同的坐标系时,它有助于我感知我想创建的内容。 例如,假设您要创建一个包含填充的 24px × 30px SVG 图标。 最好的方法是使用完全相同的大小——一个宽度为 24 像素、高度为 30 像素的 SVG 文档。 如果比例最终不正确,则以后可以随时调整。 从这个意义上说,SVG 是宽容的。 无论您做什么,它都是可缩放的。
我们正在创建的 SVG 文档宽 256 像素,高 464 像素。
绘制模型
创建模型时,我们需要考虑放置节点的位置以及要使用多少个节点。 这很重要。 这是我们为动画奠定基础的地方。 建模是变形的全部内容。 我们有一组节点转换为另一组节点。 这些节点集合需要具有完全相同的节点数。 其次,这些集合应该以某种方式相关联。
如果未仔细考虑矢量形状之间的关系,动画将不完美。 每个节点都会影响动画。 它们的位置和曲率需要恰到好处。 有关 SVG 路径中节点构造方式的更多详细信息,请参阅 MDN 上对贝塞尔曲线的解释。
其次,我们需要同时考虑两种形状。 一个矢量可能包含在另一个矢量中找不到的部分。 或者,两个模型之间可能存在其他差异。 对于这些情况,在某些地方插入额外的节点可能是一个好主意。 最好是制定策略。 比如,这个角到那里,这条直线凸起成曲线等等。
我整理了一个笔来说明当集合相关性差与准确设计时是什么样子。 在下面的示例中,左侧的变形效果中的节点是随机放置的。 构成数字一和二的节点没有关系。 在右侧示例中,节点的放置计划得更仔细。 这导致了更连贯的体验。
第一个模型
线条工具是我们用来绘制第一个矢量形状的工具。 由于我们正在创建的模型更抽象,因此稍微宽容一些。 我们仍然需要考虑放置和曲率,但它允许更多的随意性。
对于矢量和大小,创建变形模型也是如此。 这是一个迭代过程。 如果第一次不正确,我们总是可以返回并进行调整。 通常需要一两次迭代才能使动画闪耀。 以下是模型完成时的样子。

结果是一个具有八个节点的平滑抽象 SVG 形状。
第二个和第三个模型
第一个模型完成后,就该绘制第二个模型(我们也可以将其视为状态)。 这是第一组将变形到的形状。 这可能是最终状态,即单个变形效果。 或者它可能是途中的一步,比如关键帧。 在我们正在查看的情况下,有三个步骤。 同样,每个模型都必须与前一个模型相关联。 确保模型匹配的一种方法是将第二个矢量创建为第一个矢量的副本。 通过这种方式,我们知道模型具有相同的节点数以及相同的外观和感觉。
小心编辑器。 矢量编辑器通常会针对文件大小和格式进行优化。 保存更改时,它很可能会使模型不兼容。 我习惯在保存文件后检查 SVG 代码。 如果您熟悉路径描述符格式,它也会有所帮助。 如果您不习惯它,它有点神秘。 在矢量编辑器的首选项中禁用优化可能也是一个好主意。

对第三个形状重复上述过程。 复制、重新定位所有节点,并设置第三种颜色。
灯光,摄像机…开拍!
创建模型后,我们已经完成了大部分工作。 现在是时候看看动画部分了。 React-spring 带有一组我们可以用于动画和变形的钩子。 useSpring
是此示例中效果的完美候选者。 它旨在用于像我们正在创建的这样的单个动画。 以下是使用useSpring
钩子启动动画的方法。
const [{ x }, set] = useSpring(() => ({
x: 0,
}));
上面为我们提供了一个动画属性x
,围绕它构建我们的变形效果。 这些动画属性的一大优点是我们可以更改它们以创建几乎任何类型的动画。 如果值不正确,我们可以通过插值来更改它。
第二个参数,set
函数,允许我们触发更新。 下面是一段显示其用法的代码片段。 它使用来自react-use-gesture库的手势处理程序useDrag
更新动画值x
。 我们可以在 react-spring 中以多种方式触发动画。
const bind = useDrag(
({ movement: [x] }) => {
// ...
set({ x });
},
);
现在,我们已经准备好将我们的模型(路径描述符)与标记结合起来。 通过将animated
关键字添加到 JSX 代码中,我们激活了 react-spring 的动画系统。 使用插值调用to
(以前称为interpolate
),我们将拖动距离转换为变形形状。 输出数组包含我们之前讨论过的模型。 为了将它们放在适当的位置,我们只需将路径描述符从 SVG 文件复制到标记中。 现在,三个不同 SVG 文件中三个不同path
元素的三个不同描述符d
组合成一个。 以下是使用 react-spring 动画驱动的 JSX 节点的样子。
<svg ...>
<animated.path
d={x.to({
range: [-400, -200, 0],
output: [
// First model
"M 157.81292,131.16918 C 128.33979,127.45582 59.004493,121.76045 53.287478,168.06051 47.570462,214.36057 86.454799,213.14326 77.881699,234.66986 69.308599,256.19646 59.042495,268.13837 67.634107,288.98209 76.225718,309.82581 103.27857,320.05328 138.34249,312.55156 173.40641,305.04984 204.93111,298.87002 208.02612,279.75926 211.12113,260.6485 189.48716,257.88808 188.5557,229.54606 187.62424,201.20404 212.01456,174.45091 200.8528,155.7634 189.69104,137.07589 187.28605,134.88254 157.81292,131.16918 Z",
// Second model
"M 157.81292,131.16918 C 128.33979,127.45582 48.756902,138.1566 53.287478,168.06051 57.818054,197.96442 75.182448,197.77187 73.782662,224.42227 72.382877,251.07266 70.314846,257.89078 72.757903,278.7345 75.20096,299.57822 88.114636,303.32873 113.94876,307.60312 139.78288,311.87751 159.84171,314.24141 176.25858,295.13065 192.67546,276.01989 203.83379,256.86332 190.60522,228.5213 177.37665,200.17928 205.866,189.8223 211.10039,171.13479 216.33478,152.44728 187.28605,134.88254 157.81292,131.16918 Z",
// Third model
"M 157.81292,131.16918 C 128.33979,127.45582 86.672992,124.83473 71.733144,166.01099 56.793295,207.18725 69.033893,203.92043 80.955976,230.57083 92.87806,257.22123 55.968217,259.9403 59.436033,279.75926 62.90385,299.57822 94.985717,299.83924 132.0922,306.16316 169.19868,312.48708 186.48544,320.38997 198.80328,288.98209 211.12113,257.57422 199.73475,245.59097 195.72902,217.24895 191.72328,188.90693 209.96504,178.54995 215.19943,159.86244 220.43382,141.17493 187.28605,134.88254 157.81292,131.16918 Z",
],
})}
/>
</svg>
让我们看看标准 JSX path
元素与我们当前拥有的元素之间的区别。 为了使变形动画到位,我们有
- 添加了
animated
关键字以使 JSXpath
元素使用 React spring 进行动画, - 将描述符
d
从字符串更改为 React spring 插值,以及 - 将距离
x
转换为三个path
描述符之间的关键帧动画。
开发环境
我还没有找到用于处理 SVG 的完美开发环境。目前,我一直在矢量编辑器、IDE 和浏览器之间切换。这涉及到一些复制和冗余操作。它并不完美,但它有效。我过去曾尝试过一些解析 SVG 的脚本。但我仍然没有找到可以应用于所有场景的方案。也许只是我做错了。如果不是,如果 SVG 的 Web 开发能够更加无缝,那就太好了。
开始吧!
最后但并非最不重要的一点,演示!
感谢您的阅读!
我最近也一直在大量使用 SVG 动画。如果您喜欢这种东西,我强烈建议您查看 GreenSock JS。
它们众多令人惊叹的扩展之一是 MorphSVG
感谢这篇文章。
此编辑器可以帮助您深入了解 SVG 的结构
https://yqnn.github.io/svg-path-editor/