SVG动画优化全攻略:从设计到性能提升

news/2025/9/19 8:04:12/文章来源:https://www.cnblogs.com/qife122/p/19100018

粉碎动画第四部分:优化SVG

SVG动画让我回想起童年观看的汉纳-巴伯拉卡通片。像《Wacky Races》、《The Perils of Penelope Pitstop》当然还有《瑜伽熊》这样的节目,激励我使用CSS、SVG和SMIL动画精心重现一些经典卡通标题。

但让动画快速加载和流畅运行需要的不仅仅是怀旧。它需要简洁的设计、精简的代码和使复杂SVG更易于动画化的流程。以下是我的方法。

现在有一个网站可以查看我所有的卡通标题。(大预览)

无论是个人项目还是商业工作,良好准备SVG可确保它们可访问。优化它们确保快速加载,特别是在移动设备上,仔细考虑它们的结构使维护更容易。我开发了一个平衡视觉效果与可访问性和性能的过程,并使复杂SVG更易于使用。

因此,为解释我的过程,我选择了1960年1月首次播出的《瑜伽熊秀》中名为“被施了魔法的熊”的一集。在这个故事中,瑜伽偷了一把女巫的扫帚来帮助他抓“野餐”篮子。

“嘿,嘿,嘿!”

《瑜伽熊秀》©️ 华纳兄弟娱乐公司。(大预览)

保持简洁并设计时考虑优化

保持简洁是制作优化并准备好动画的SVG的关键。像Adobe Illustrator这样的工具将位图图像转换为矢量,但输出通常包含太多无关的组、层和蒙版。相反,我在Sketch中开始清理,使用参考图像,并使用钢笔工具创建路径。

提示:Affinity Designer(英国)和Sketch(荷兰)是Adobe Illustrator和Figma的替代品。两者都是独立的,总部位于欧洲。自从Adobe停止Fireworks以来,Sketch一直是我的默认设计应用程序。

从轮廓开始

对于这些卡通标题插图,我首先使用钢笔工具绘制黑色轮廓,尽可能少地使用锚点。形状的点越多,文件就越大,因此简化路径和减少点数可以使SVG小得多,通常没有明显的视觉差异。

左:160个锚点。右:80个点。(大预览)

考虑到这个瑜伽插图的某些部分最终会被动画化,我将这个被施了魔法的熊的身体、头部、衣领和领带的轮廓分开,以便我可以独立移动它们。头部可能会点头,领带可能会飘动,而且像那些经典卡通一样,瑜伽的衣领会隐藏它们之间的连接。

身体、头部、衣领和领带以及扫帚的单独轮廓。(大预览)

绘制简单的背景形状

轮廓就位后,我再次使用钢笔工具绘制新形状,用颜色填充区域。这些颜色位于轮廓后面,因此不需要完全匹配。锚点越少,文件大小越小。

左:原始矢量艺术作品,8 KB。右:使用Adobe Illustrator简化,2 KB。(大预览)

遗憾的是,Affinity Designer和Sketch都没有可以简化路径的工具,但如果你有,使用Adobe Illustrator可以从这些背景形状中再削减几千字节。

Adobe Illustrator:对象→路径→简化。(大预览)

优化代码

不仅仅是元数据使SVG更臃肿。从设计应用程序导出的方式也会影响文件大小。

准备好进行优化的矢量艺术作品。(大预览)

从Adobe Illustrator导出这些简单的背景形状默认包括不必要的组、蒙版和臃肿的路径数据。Sketch的代码也好不了多少,即使在其SVGO压缩器代码中也有很大的改进空间。我依赖Jake Archibald的SVGOMG,它使用SVGO v3,并始终提供最佳优化的SVG。

Jake Archibald的SVGOMG在线优化工具。(大预览)

分层SVG元素

我为动画准备SVG的过程远远超出了绘制矢量和优化路径的范围——还包括我如何构建代码本身。当每个视觉元素都塞进一个SVG文件中时,即使是优化的代码也可能难以导航。定位特定路径或组通常感觉像大海捞针。

瑜伽熊标题卡设计由Lawrence Goble(1958年)。卡通标题重现。(大预览)

这就是为什么我分层开发SVG,一次导出一组元素并进行优化——始终按照它们在最终文件中出现的顺序。这让我可以通过粘贴每个清理过的部分来逐步构建主SVG。例如,我从像这样的渐变和标题图形背景开始。

渐变背景和标题图形。(大预览)

现在,我可以轻松识别背景渐变的路径及其相关的linearGradient,并查看包含标题图形的组,而不是面对一堵SVG代码墙。我借此机会向代码添加注释,这将使将来编辑和添加动画更容易:

<svg ...><defs><!-- ... --></defs><path fill="url(#grad)" d="…"/><!-- 标题图形 --><g><path … /><!-- ... --> </g>
</svg>

复制

带有高斯模糊的轨迹。(大预览)

接下来,我添加瑜伽飞行扫帚的模糊轨迹。这包括定义一个高斯模糊滤镜并将其路径放置在背景和标题层之间:

<svg ...><defs><linearGradient id="grad" …>…</linearGradient><filter id="trail" …>…</filter></defs><!-- 渐变 --><!-- 轨迹 --><path filter="url(#trail)" …/><!-- 标题图形 -->
</svg>

复制

瑜伽熊的魔法星星。(大预览)

然后是魔法星星,以相同的顺序方式添加:

<svg ...><!-- 渐变 --><!-- 轨迹 --><!-- 星星 --><!-- 标题图形 -->
</svg>

复制

为了保持一切有序并准备好动画,我创建一个空组来容纳瑜伽的所有部分:

<g id="yogi">...</g>

复制

顺序添加瑜伽熊的组成部分。(大预览)

然后我从地面开始构建瑜伽——从背景道具开始,比如他的扫帚:

<g id="broom">...</g>

复制

然后是分组元素,包括他的身体、头部、衣领和领带:

<g id="yogi"><g id="broom">…</g><g id="body">…</g><g id="head">…</g><g id="collar">…</g><g id="tie">…</g>
</g>

复制

瑜伽熊标题卡设计由Lawrence Goble(1958年)。卡通标题重现。(大预览)

由于我从相同大小的画板导出每个层,我不需要担心以后的对齐或定位问题——它们都会自动就位。通过这种方式分层元素,我保持代码清洁、可读和逻辑有序。这也使动画更流畅,因为每个组件更容易识别。

使用重用元素

当重复形状被重复使用时,SVG文件可能迅速变得臃肿。我对“被施了魔法的熊”标题卡的重现包含80颗星星,分为三种尺寸。将所有形状合并为一个优化路径将使文件大小降至3KB。但我想动画单个星星,这几乎会将其增加到5KB:

<g id="stars"><path class="star-small" fill="#eae3da" d="..."/><path class="star-medium" fill="#eae3da" d="..."/><path class="star-large" fill="#eae3da" d="..."/><!-- ... -->
</g>

复制

将星星的填充属性值移动到它们的父组会稍微减少总体重量:

<g id="stars" fill="#eae3da"><path class="star-small" d="…"/><path class="star-medium" d="…"/><path class="star-large" d="…"/><!-- ... -->
</g>

复制

瑜伽熊闪闪发光的星星。(大预览)

但更高效和可管理的选项是将每个星星尺寸定义为可重用模板:

<defs><path id="star-large" fill="#eae3da" fill-rule="evenodd" d="…"/><path id="star-medium" fill="#eae3da" fill-rule="evenodd" d="…"/><path id="star-small" fill="#eae3da" fill-rule="evenodd" d="…"/>
</defs>

复制

通过这种设置,更改星星设计只需更新其模板一次,每个实例都会自动更新。然后,我使用引用每个模板,并使用x和y属性定位它们:

<g id="stars"><!-- 大星星 --><use href="#star-large" x="1575" y="495"/><!-- ... --><!-- 中星星 --><use href="#star-medium" x="1453" y="696"/><!-- ... --><!-- 小星星 --><use href="#star-small" x="1287" y="741"/><!-- ... -->
</g>

复制

这种方法使SVG更易于管理、加载更轻、迭代更快,特别是在处理数十个重复元素时。最重要的是,它在不牺牲灵活性或性能的情况下保持标记清洁。

添加动画

跟随瑜伽偷来的扫帚的星星为动画带来了很多个性。我希望它们在深蓝色背景上以看似随机的模式闪烁,因此我首先定义一个关键帧动画,循环通过不同的不透明度级别:

@keyframes sparkle {0%, 100% { opacity: .1; }50% { opacity: 1; }
}

复制

接下来,我将这个循环动画应用于星星组内的每个use元素:

#stars use {animation: sparkle 10s ease-in-out infinite;
}

复制

创造令人信服的闪烁秘密在于变化。我使用nth-child选择器在星星之间错开动画延迟和持续时间,从最快和最频繁的闪烁效果开始:

/* 快速,频繁 */
#stars use:nth-child(n + 1):nth-child(-n + 10) {animation-delay: .1s;animation-duration: 2s;
}

复制

从那里,我分层添加额外的时间来混合事物。有些星星闪烁缓慢而戏剧性,其他更随机,有各种节奏和暂停:

/* 中等 */
#stars use:nth-child(n + 11):nth-child(-n + 20) { ... }/* 缓慢,戏剧性 */
#stars use:nth-child(n + 21):nth-child(-n + 30) { ... }/* 随机 */
#stars use:nth-child(3n + 2) { ... }/* 交替 */
#stars use:nth-child(4n + 1) { ... }/* 分散 */
#stars use:nth-child(n + 31) { ... }

复制

通过深思熟虑地构建SVG和重用元素,我可以构建看起来复杂而没有臃肿代码的动画,使即使是改变不透明度这样的简单效果也能闪烁。

细微的动作赋予瑜伽熊生命。(大预览)

然后,为了增加真实感,我让瑜伽的头部摇晃:

@keyframes headWobble {0% { transform: rotate(-0.8deg) translateY(-0.5px); }100% { transform: rotate(0.9deg) translateY(0.3px); }
}#head {animation: headWobble 0.8s cubic-bezier(0.5, 0.15, 0.5, 0.85) infinite alternate;
}

复制

他的领带飘动:

@keyframes tieWave {0%, 100% { transform: rotateZ(-4deg) rotateY(15deg) scaleX(0.96); }33% { transform: rotateZ(5deg) rotateY(-10deg) scaleX(1.05); }66% { transform: rotateZ(-2deg) rotateY(5deg) scaleX(0.98); }
}#tie {transform-style: preserve-3d;animation: tieWave 10s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite;
}

复制

他的扫帚摆动:

@keyframes broomSwing {0%, 20% { transform: rotate(-5deg); }30% { transform: rotate(-4deg); }50%, 70% { transform: rotate(5deg); }80% { transform: rotate(4deg); }100% { transform: rotate(-5deg); }
}#broom {animation: broomSwing 4s cubic-bezier(0.5, 0.05, 0.5, 0.95) infinite;
}

复制

最后,瑜伽本人在他的魔法扫帚上飞行时轻轻旋转:

@keyframes yogiWobble {0% { transform: rotate(-2.8deg) translateY(-0.8px) scale(0.998); }30% { transform: rotate(1.5deg) translateY(0.3px); }100% { transform: rotate(3.2deg) translateY(1.2px) scale(1.002); }
}#yogi {animation: yogiWobble 3.5s cubic-bezier(.37, .14, .3, .86) infinite alternate;
}

复制

所有这些细微的动作赋予瑜伽生命。通过开发结构化的SVG,我可以创建充满个性的动画,而无需编写一行JavaScript。

自己尝试:
参见Pen:被施了魔法的熊CSS/SVG动画 [forked] by Andy Clarke。

结论

无论你是重现经典标题卡还是为界面动画图标,原则都是相同的:

  • 开始清洁,
  • 早期优化,以及
  • 一切结构都考虑动画。

SVG提供令人难以置信的创作自由,但只有在保持精简和可管理的情况下。当你像生产单元一样规划过程——逐层、逐元素——你将花更少的时间解开代码,更多的时间赋予作品生命。

(gg, yk)
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
公众号二维码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/907607.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析:Go 1.25.1 自定义包调用

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

MX 练石 2026 NOIP #7

0 + 10 + 10 + 0 = 20, Rank 152/199.好难好难好难好难,为数不多的罚坐了。 4h20min 怒砍 20pts /ohMX 练石 2025 NOIP #6 链接:link 题解:link 时间:4h20min (2025.09.18 13:50~18:10) 题目数:4 难度:A B C D估…

多元积性函数

定义:若函数 \(f(n,m)\) 满足 \(ab \perp xy \Rightarrow f(ax,by)=f(a,b)f(x,y)\),则称 \(f\) 为二元积性函数。 积性分解:将 \(x=\prod p_i^{\alpha _i},y=\prod p_i^{\beta _i}\),则有 \(f(x,y)=\prod f(p_i^{\…

国内AI云市场:挤不进前三,生存将成问题!

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087中国AI云市场已形成“一大四强”的格局,阿里云以35.8%的份额独占鳌…

P14053 [SDCPC 2019] Median 题解

P14053 [SDCPC 2019] Median 题解P14053 [SDCPC 2019] Median 题解 一道水题。 观察题意,很快我们可以发现,对于元素 \(i\),其合不合法取决于一定大于 \(i\) 的数的个数与一定小于 \(i\) 的数的个数。 这时,我们只…

lQueryDef查询Evaluate报该几何不包含M值问题。

地理数据库既包括空间,又包括属性,属性类似于SQL表,理论上支持标准SQL查询。lQueryDef接口提供了高效查询方法,适用于对属性表或要素类的属性进行筛选和检索。 问题描述 一个简单的面积求和示例如下:IQueryDefFac…

我的首个RCE漏洞发现之旅:Apache ActiveMQ远程代码执行实战

本文详细讲述了作者如何通过系统化的子域名枚举和端口扫描,发现Apache ActiveMQ的CVE-2023-46604远程代码执行漏洞的全过程,包含具体的工具使用方法和实战技巧。我的首个RCE漏洞发现经历 大家好!在这篇文章中,我将…

北京市社保费用差额补缴计算工具

北京市社保费用差额补缴计算工具9月18日北京市发布了社会保险缴费工资基数上下限调整的通告,自2025年7月起,社保基数下限由原来的 6821元提高到7162元。 这样一来,之前已经缴了7月份社保且社保基数不到7162元的就需…

使用自签名SSL证书有什么风险?

自签名SSL证书,指的是由用户自行生成密钥对并予以签名的证书,无需经由第三方权威证书颁发机构(CA)审核。鉴于其具备零成本、生成便捷的特性,该证书常被应用于个人测试、内部临时服务等非生产场景。 然而,相较于权…

CDN可以使用iTrustSSL通配符证书吗?

CDN,即内容分发网络,它是一种通过在多个地理位置分散部署服务器节点,将网站的内容缓存并分发到离用户最近的节点上,从而显著提高网站内容的访问速度、降低延迟,并减轻源服务器负载的技术架构。借助CDN,网站能够更…

[ssh]:SecureCRT的配置

[ssh]:SecureCRT的配置[ssh]:SecureCRT的配置一、有用配置1.  Terminal  ->  Emulation 2.  Terminal  ->  Apperance 二、参考1.  无.本文由 lnlidawei 原创、整理、转载,本文来自于【博客园】…

OpenCvSharp基于颜色反差规避FBA面单贴标

01 规避原理 1.抠图,根据色差或者根据固定包裹位置以及包裹尺寸抠出纸箱图片 2.色差,获取纸箱上所有背景色的灰度值 3.采图,采集大量视野相同,光源相同面单的色差灰度值,整理区间 4.取反,所有非面单灰度值区间的…

[LeetCode] 3408. Design Task Manager

There is a task management system that allows users to manage their tasks, each associated with a priority. The system should efficiently handle adding, modifying, executing, and removing tasks. Implem…

从0开始的游戏全栈开发工程师学习记录

从0开始的游戏全栈开发工程师学习记录开始学习游戏开发

Torrent File Editor 1.0.0

https://torrent-file-editor.github.io/ 下载:https://github.com/torrent-file-editor/torrent-file-editor/releases

US$428 XTOOL X-100 PAD Tablet Key Programmer with EEPROM Adapter Support Special Functions

XTOOL X100 PAD Tablet Key Programmer with EEPROM Adapter Support Special FunctionsNotice: 1. Language: English, Spanish, French, German, Norwegian, Russian, Persian, Arabic, Polish, Hindi and Portugues…

US$49 Multi-languages Smart Zed-Bull With Mini Type No Tokens Needed

Multi-languages Smart Zed-Bull With Mini Type No Tokens NeededTop 6 Reasons to Get the Smart Zed-Bull:1.Language:English, Turkish, Italian, Spanish and Portugues2. No Tokens Needed!3. New: Support 8C a…

US$149 Foxwell NT630 Elite ABS and Airbag Reset Tool with SAS

Foxwell NT630 Elite ABS and Airbag Reset Tool with SASUpdate Online Free Lifetime.Support Multi-Language: English, French, Spanish, Hungrian, Korean, Japanese & GermanFoxwell NT630 Elite Features a…

AI CodeReview + Devops协同

在软件开发团队里,Code Review 是非常重要的一个质量保障环境。好的 Code Review 能促进团队成长,差的 Code Review 形同流水。而在有了 LLM 之后,事情又发生了一些微妙的变化:随着代码产量上升,需要 review 的代…

【API接口】最新可用手机号归属地查询接口

最新可用手机号归属地查询接口,查询手机号码归属地、所属号段、手机卡类型、运营商等信息 使用之前您需要先去注册下key 申请地址: https://www.52api.cn 接口地址:https://www.52api.cn/api/mobile_location 返回…