如果您熟悉 SVG 和 CSS 动画并开始经常使用它们,那么在开始工作之前,您可能需要记住一些想法。 本文将介绍如何使用 **<use>
元素、CSS 变量和 CSS 动画** 来构建和优化您的代码。
第一部分:SVG <use> 元素
如果您是一位喜欢保持代码 DRY 或非常喜欢 Sass/CSS 变量的开发人员,那么您很有可能会喜欢这个标签。
假设您的图形中多次重复了一个元素。 与在 SVG 中多次重复代码的复杂部分相比,您可以定义此部分一次,然后使用 <use> 元素将其克隆到文档中的其他位置。 这不仅会减少大量的代码,还会使您的标记更简单,更易于操作。
要开始实施 <use>
元素,请转到您的 SVG 并按照以下步骤操作
- 识别要克隆的代码部分
- 为此部分添加一个 ID
- 在您的
<use>
标签中链接它,如下所示:<use xlink:href="#id"/>
就是这样! 您的新克隆已准备就绪,现在您可以更改其属性(例如 x
和 y
位置)以满足您的需求。
让我们深入研究一个非常方便的示例
我想分享这个真实案例,我需要动画制作一个由小立方体单元组成的巨大立方体。(想象一下经典的 魔方。)
我们将首先使用基本形状和变换在 SVG 中绘制立方体单元
<svg viewBox="-130 -20 300 100">
<g id="cube">
<rect width="21" height="24" transform="skewY(30)"/>
<rect width="21" height="24" transform="skewY(-30) translate(21 24.3)"/>
<rect width="21" height="21" transform="scale(1.41,.81) rotate(45) translate(0 -21)"/>
</g>
</svg>
请注意,形状分组在一个 <g>
元素中,以便我们可以将 ID 添加到整个图形。
接下来,让我们构建一个更大的立方体,克隆此单元。 首先,我们需要将上一个示例中的立方体包装在 SVG 内部的 <defs>
标签中。 在 <defs>
元素中,我们可以放置任何想要重用的内容,这可以是一个形状、一个组、一个渐变... 几乎任何 SVG 元素。 除非我们在该标签之外使用它们,否则它们不会在任何地方渲染。
然后,我们可以使用其 ID 链接单元体任意多次,并像这样更改每个克隆的 x
和 y
位置
<use xlink:href="#cube" x="142" y="124"/>
<use xlink:href="#cube" x="100" y="124"/>
<!-- ... -->
现在我们必须定位每个立方体,记住最后一个元素将出现在最前面,之后我们将准备好第一个大立方体!
xlink:href
自 SVG2 以来已弃用,但出于兼容性目的最好使用它。 在现代浏览器中,您只需使用 href,但我已在 Safari 上对其进行了测试,在撰写本文时它在那里无法使用。 如果您使用 xlink:href
,请确保在您的 SVG 标签中包含此命名空间:xmlns:xlink="http://www.w3.org/1999/xlink"
(如果您决定使用 href,则无需它)。
第二部分:使用 CSS 变量将不同的样式应用于重用图形
我为立方体选择了主色,它是一种较浅和较深的侧面阴影和描边颜色。 但是如果我们想让第二个立方体变成不同的颜色怎么办?
我们可以用 CSS 变量替换填充和描边,以使这些属性更灵活。 这样,我们将能够使用另一个调色板重用相同的立方体单元(而不是为第二个立方体定义第二个单元,该单元具有不同的颜色)。
为什么不向新的立方体添加一个类并用 CSS 更改填充颜色? 我们会这样做,但首先,尝试检查一个 <use>
元素。 您会注意到它在 Shadow DOM 中呈现。 这意味着它不受脚本和样式的影响,就像普通 DOM 中的元素一样。 您在 <defs>
中图形中定义的任何值都将被其所有实例继承,您将无法用 CSS 重写它们。 但是,如果您用变量替换这些值,那么您就可以 在 CSS 中控制它们。
在我们的立方体单元中,我们将遍历每个侧面并将填充和描边值替换为语义变量名称。
例如,这
<rect fill="#00affa" stroke="#0079ad" />
... 可以用以下内容替换
<rect fill="var(--mainColor)" stroke="var(--strokeColor)" />
从这里开始,我们必须复制 SVG 以构建第二个立方体。 但是,如果我们将两者都保存在同一个文档中,则无需复制 <defs>
。 我们可以向每个 SVG 添加一个类,并通过 CSS 控制调色板,重新定义变量的值。
让我们为蓝色立方体创建一个调色板,为粉色立方体创建另一个调色板
.blue-cube {
--mainColor: #009CDE;
--strokeColor: #0079ad;
--lightColor: #00affa;
--darkColor: #008bc7;
}
.pink-cube {
--mainColor: #de0063;
--strokeColor: #ad004e;
--lightColor: #fa0070;
--darkColor: #c7005a;
}
这样,我们可以添加任意数量的立方体,并从一个地方更改所有颜色。
第三部分:重用动画
此实例的想法是在悬停时打破立方体 - 类似于爆炸视图,因此当我们将光标放在立方体上时,一些部件会从中心移开。
让我们从定义两个动作开始,一个用于每个轴:move Y
和 move X
。 通过将动画划分为动作,我们将能够在每个立方体中重用它们。 动画将包括将立方体从其初始位置移动到一个方向上 30px 或 50px 处。 我们可以使用变换平移 (X
或 Y
) 来实现这一点。 例如
@keyframes moveX {
to { transform: translateX(-35px); }
}
但是,如果我们想重用此动画,最好用变量替换数值,如下所示
@keyframes moveX {
to { transform: translateX(var(--translate, 35px)); }
}
如果未定义变量,则默认值为 35px。
现在我们需要至少一个类来绑定到动画。 但是,在这种情况下,我们需要两个类来在 x 轴上移动立方体:.m-left
和 .m-right
。
.m-left, .m-right {
animation: 2s moveX alternate infinite;
}
为了使立方体向左移动,我们需要一个负值,但我们也可以声明一个不同的数字。 我们可以像这样在 .m-left
类内部定义我们的变量
.m-left { --translate: -50px; }
这里发生的事情是,当我们将类 .m-left
添加到一个元素时,这将播放动画 moveX
(在 @keyframes
中定义的动画),该动画将持续两秒钟,并在 x 轴上平移,到达左侧 -50px 的新位置。 然后,动画交替方向,以便它从最后一个位置移动,并再花两秒钟回到其原始状态。 等等,因为它是一个无限循环。
我们可以为 .m-right
类声明另一个变量,但如果我们不这样做,请记住它将采用我们最初声明的 35px。
默认的 animation-play-state
值是运行的,但我们可能不想让立方体一直移动。 在网站上使用一些附近的相关内容时,这会非常分散注意力,而且令人讨厌。 因此,让我们尝试仅在悬停时播放动画,通过添加以下内容
svg:hover .m-left {
animation: 2s moveX alternate infinite;
}
您可以自己尝试一下,会发现每次将光标从立方体移开时,动画都会非常快地跳转到初始状态。 为避免这种情况,我们可以在动画速记的末尾添加值 paused
.m-left {
animation: 2s moveX alternate infinite paused;
}
现在动画已暂停,但通过添加以下 CSS 行,它将在悬停时运行
svg:hover * {
animation-play-state: running;
}
我们可以将每个类应用于 SVG 中的不同元素。 在第一个蓝色立方体中,我们移动单个立方体; 在第二个立方体中,我们将这些类应用于立方体组。
最后一件事...
直到后来我才意识到,我可以重用一个单元来构建所有单元。 我对小立方体进行了处理,使其具有等距性,以便它可以轻松地与旁边的其他立方体对齐。 此时,我的单元是一个 <path>
,但我决定用 SVG 形状替换它,以减少代码并获得更清晰的标记。
我了解到,在绘制每个形状并处理大量代码之前,最好花一些时间分析一下 SVG 可以做什么。 这可能需要更多时间,但在长远来看会为您节省大量时间和精力。
这太棒了!
感谢您让它如此清晰
感谢 Ami!我真的很感谢!
我昨天才遇到这个问题!不幸的是,我需要在我的 SVG 中动画化贝塞尔曲线,这(根据我的研究)意味着 CSS 不够用,我必须使用 SMIL(例如,“animate” 元素)。
据我所知,您无法使用“use”元素来 DRY 化“animate”元素。希望这对未来的读者有所帮助!
我不确定这是否是您要找的,但如果您定义的两个路径的点数量相同,您可以在用贝塞尔曲线(三次或二次)命令定义的路径之间进行动画化。您可以转换路径定义,使其仅使用三次命令,并且点数量与另一个路径相同。GSAP 有其(付费)MorphSVG 插件,或者您可以使用此包:https://codepen.io/creative-wave/full/qBBWdQO。
SMIL 在 Chrome 中不再过时,但我不会太建议人们使用它。
感谢您分享,Brad!我从未尝试在 SVG 中动画化贝塞尔曲线,我读到即使使用 CSS,动画化
<use>
元素也有很多限制。我很想看看您使用 SMIL 动画的结果!不错的包,Guillaume!
不幸的是,“use”元素的性能比其他 SVG 元素差:https://stackoverflow.com/q/8604999/2065702
有趣的帖子,Zach!我喜欢为此缓存外部文件的解决方案。
在 SVG 中重用数据块的另一种方法:XML 实体。当在 HTML 外部使用时,SVG 基本上只是 XML,并具有其所有奇怪的优势(XSLT 任何人?)。请参阅 示例笔,它很可能过度使用此技巧,+ 描述性“文章”笔 是为 @gabi 制作的,记得是过去的时候;原谅蹩脚的语言。
我还在 优秀的 Ana Tudor 的 CSS 技巧的 SMIL 改编版 中利用了它。
变形 SVG 网格令人惊叹,我非常喜欢示例和描述性文章。我以前从未使用过实体,感谢您分享!
使用中的变量技巧对我来说是一个发现。谢谢!
我很高兴这帮助您发现了它!CSS 变量和 currentColor 是我最常用的变量。Sara Soueidan 的链接文章非常完整地介绍了这些主题!
这太天才了!谢谢 :)
谢谢,Pedro!