以下是 Jack Doyle 的客座文章,他是 GreenSock 动画平台 (GSAP) 的作者。Jack 长期浸淫于网页动画领域,致力于使其更简单、更出色。他之前也曾在这里发表文章,讨论 JavaScript 动画如何成为 性能最佳 的选择(谷歌甚至 推荐 使用它)。这次,他重点介绍了 SVG 动画,以及在使用 CSS 操作它们时可能会遇到的一些非常棘手的问题,以及如何解决这些问题。
SVG 如今非常流行,并且浏览器支持度普遍良好……但有一个明显的例外:CSS 变换。这在动画方面尤其令人头痛,因为缩放、位置、旋转和倾斜都是非常基础的操作。
系好安全带,因为我们将经历一段颠簸的旅程。但不用担心;这个复杂的爱情故事有一个美满的结局。在本文中,我将带您了解一些问题,然后向您展示一种技术,该技术可以协调跨浏览器的行为,并且已内置到最新版本的 GreenSock 动画平台 (GSAP) 中。
浏览器错误和不一致性
首先,查看这些动画 GIF,它们展示了在各种浏览器中(至少截至 2014 年 11 月)对两个 <rect>
元素应用 完全相同的 CSS 动画 的效果。
查看 GreenSock (@GreenSock) 在 CodePen 上创作的 Pen GIF:SVG + CSS 变换问题。
- IE 和 Opera 根本不认可 SVG 元素上的 CSS 变换。相反,您必须将值赋予
transform
**属性**。 - Firefox 不认可基于百分比的原点。
- 在 Safari 中放大会导致基于百分比和基于像素的原点之间的同步出现问题。
- Firefox 不识别像
"right bottom"
这样的基于关键字的原点,而 Safari 在缩放比例不是 100% 时会更改它们。 - 在所有浏览器中,基于像素的原点对于 SVG 元素的测量方式与其他 DOM 元素(如下所示)不同。
这是一个演示这些错误的视频。
将 transform-origin
视为所有旋转和缩放操作围绕的点,就像一个别针固定在那里一样。
我的变换原点在哪里?
通常,如果您想围绕元素的中心进行旋转或缩放,则会设置 transform-origin: 50% 50%
。百分比相对于元素自身的原生大小,因此其宽度和高度的 50% 正好落在中间。所有主要浏览器都支持常规 DOM 元素的基于百分比的原点,但只有少数浏览器支持 SVG 元素的基于百分比的原点。
基于像素的值呢?支持度非常好,但还有一个问题:如下所示,SVG 相对于父 SVG 的 0,0 坐标来测量基于像素的值,而其他所有 DOM 元素(如 <div>
)则相对于其自身的左上角进行测量。因此,如果您想将其居中,则必须进行计算以在 SVG 中绘制坐标(您可以为此使用 getBBox()
JS)。因此,您不能对常规 DOM 元素和 SVG 元素应用相同的 CSS 并期望它们的行为相同。
查看 GreenSock (@GreenSock) 在 CodePen 上创作的 Pen SVG 变换原点演示。
解决方案
如果我有一个在这里可以使用的纯 CSS 技巧就好了,但由于 IE 和 Opera 完全忽略了 SVG 元素上的 CSS 变换,并且大多数其他浏览器中都存在错误,因此我们将依靠 JavaScript 的一项不可思议的优势:它的灵活性。即使每个浏览器在下周都修复了它们的错误,我们仍然无法回避 SVG 规范处理基于像素的变换原点的方式与其他 DOM 元素完全不同的这一事实,因此 CSS 变换受限于该规范。据我所知,唯一可行的长期解决方案是基于 JS 的解决方案。此外,JS 还为我们提供了许多其他好处,但这将是另一篇文章的主题。
**目标:**能够以与“普通”DOM 元素相同的方式对 SVG 元素的各种变换属性(旋转、缩放、位置和倾斜)进行动画处理,同时在所有主要浏览器中提供相同的效果。我们还应该能够使用标准的 CSS 类值(包括百分比、像素或像 "right bottom"
这样的关键字)来设置变换原点。哦,它必须快速。
如果您还不熟悉 GreenSock 的 GSAP,它是一个高性能、专业级的 JavaScript 动画库,具有无与伦比的排序工具、运行时控件和灵活性(可以对 JavaScript 可以触及的任何内容进行动画处理)。谷歌推荐 使用它进行基于 JS 的动画,并且它的速度比 jQuery 快 20 倍。
以下是 GSAP 内部采用的技术概述(我们不会深入研究矩阵数学,因为这超出了本文的范围)。下面的演示说明了当您尝试通过指定 "50% 50%"
的变换原点来围绕其中心旋转 SVG <rect>
时,GSAP 会做什么。
查看 GreenSock (@GreenSock) 在 CodePen 上创作的 Pen SVG 变换原点解决方案。
生成的 matrix() 字符串应馈送到元素的 CSS 样式或 transform
属性,具体取决于特定浏览器的需要。
如果您不明白,没关系——所有这些操作在 GSAP 中都是自动为您完成的。
结果
我们不仅获得了跨浏览器的协调行为和 SVG 和非 SVG 元素的一致动画 API,而且我们必须编写的动画代码与(有问题的)纯 CSS 动画相比非常少。
TweenMax.to("#svg, #div", 2, {
rotation:360,
transformOrigin:"50% 50%"
});
这是一个演示了多个原点序列的演示。注意,SVG <rect>
和 <div>
的效果都相同。
查看 GreenSock (@GreenSock) 在 CodePen 上创作的 Pen SVG CSS 变换时间轴。
使用 GSAP 为 SVG 制作动画的技巧
我不会在这里解释 GSAP 的 API(请参阅 入门文章),但我将提供一些与 SVG 相关的技巧。在 github 上获取 GSAP 或从 GreenSock.com 下载它。
- 与在 CSS 中将所有变换组件组合到单个“transform”字符串中不同,GSAP 允许您独立定义每个组件,并且您无需担心操作顺序(它始终是一致的)。还要记住,GSAP 使用
x
和y
来表示translateX()
和translateY()
,使用rotation
来表示rotate()
。其他所有内容都相同(scaleX
、scaleY
、skewX
和skewY
)。#yourID { animation-name: myAnimation; animation-duration: 2s; animation-timing-function: ease-out; transform-origin: right bottom; } @keyframes myAnimation { from {transform: none;} to {transform: rotate(270deg) scaleX(0.5) translateX(100px);} }
TweenMax.to("#yourID", 2, { rotation:270, scaleX:0.5, x:100, transformOrigin:"right bottom" });
- 您可以将原始元素作为目标(第一个参数)传递,也可以传递选择器文本或元素数组。
- 要对 SVG 的数字属性(而不是 CSS 属性)进行动画处理,请使用
attr:{}
对象,例如TweenMax.to("#circle", 2, {attr:{cx:200, cy:300, r:20}, ease:Power2.easeInOut});
- 您还可以使用3D 属性,例如
z
、rotationX
、rotationY
和perspective
,但 IE 和 Opera 等一些浏览器不会识别 SVG 元素的这些属性。 - 最好通过 GSAP 直接设置
transformOrigin
,而不是在 CSS 中设置,因为各种浏览器通过getComputedStyle()
报告它们的方式不一致。 - 您可以对几乎任何 CSS 值进行动画处理,包括像
fill
和stroke
这样的颜色属性。 - 如果您想进行大量排序,我强烈建议观看 这里 和 这里 的视频。排序是 GSAP 最重要的优势之一。
更多资源
- Sara Soueidan 撰写了一篇关于通过 SMIL 动画 SVG 的精彩文章。
- 在 CSS-Tricks 上有一个关于 SVG 相关资源的超长列表。
- 查看 Joni Trythall 漂亮的SVG 书写口袋指南。
- 神话终结:CSS 动画与 JavaScript
对于 Firefox,transform-origin 根本不支持 SVG。它曾被实现,但由于性能问题而被撤回。最近有一些工作来调整补丁并重新发布。如果有人想研究它,请参阅错误 923193。
我很想看到更多关于跨浏览器 SVG 不一致性的文章。我自己也遇到了一些。
你的解释方式很棒,也很容易理解。哇,可能花费了大量时间才使它变得如此吸引人和易读。很棒的工作,期待更多这样的作品。我会努力使我的作品像你一样有说服力 :)
浏览器如何渲染 SVG 动画的有趣挖掘。感谢 Jack :)
有人知道这些浏览器问题是否仅适用于内联 SVG?或者使用以 .svg 结尾的 img 标签也会有顾虑吗?
我不确定你想如何动画你的 SVG 图像,所以这里有一个两种选项的快速分解
如果你想动画作为 <img> 使用的 SVG 中的内容,你必须在 XML 标记中使用 SMIL 动画或在嵌入式样式表中使用 CSS 动画,两者都在主 .svg 文件中。没有浏览器支持图像文件中的外部资源或 JavaScript。
但是,如果你想在网页上移动整个 <img> 元素,则可以使用与任何其他类型的图像(或网页的任何其他元素)相同的 CSS 转换。CSS 动画和基于 JavaScript 的动画都应该可以工作。SMIL 不是一个选项,因为 <img> 是一个 HTML 元素,而不是一个 SVG 元素。根据生效的 box-sizing 属性,用于 transform-origin 的坐标系将基于 <img> 元素的左上角,并且默认原点将是图像的中心。
不错的工作!
一直在期待看到这个!
关于 transformOrigin:你能让它像普通的 SVG 一样工作吗?因为目前,你让它像 html 一样工作,但有时(对我来说,“此刻”)我想能够从一个点旋转东西。
当然,Otto,你可以使用 getBBox() 来计算父 SVG 画布原点的偏移量。这是一个你可以在你的补间中重复使用的简单函数
所以你只需传入 SVG 元素以及可选的围绕其旋转/缩放/倾斜的偏移量/点,它就会返回相应的 transformOrigin,例如“-150px -75px”。对于每个元素来说,它显然都是不同的,这就是为什么我将其包装在一个函数中,这样你就可以重复使用它,它会为你完成工作。这有帮助吗?
还没有尝试过,但请看一下这个代码示例……。
http://codepen.io/anon/pen/MYWjJm
正如你所看到的,我的问题有点深:我需要动画包含内容的元素。
在示例中,我添加了一个小的作为我的支点,所以我不需要你的函数。
它在 Firefox 和 Chrome 上有效,但在 IE11 上无效。
Otto,这可能应该在 GreenSock 论坛 (http://greensock.com/forums/) 而不是在评论中处理,但问题在于你的中心“g”元素在设置 x/y 时是孤儿;当 SVG 元素没有 SVG 父元素时,浏览器会以不同的方式处理它们,因此它会触发 CSSPlugin 中的逻辑以相应地评估它。我将在 1.14.3 中添加代码来解决此问题,但目前它应该像在你的代码中翻转两行一样简单。
我在两周前遇到了这个问题,也用 js 解决,但我使用了 snapSVG。它在选定对象的属性中具有 X 和 Y 中心,因此你可以轻松地获取中心并进行动画。
干杯。
Greensock 是否有任何方法可以动画路径和矩形的颜色?我尝试使用 Greensock 动画单个属性,但运气不太好。
当然,它应该像这样简单
这是一个基本的 codepen:http://codepen.io/GreenSock/pen/vEEjeQ
我注意到你所有文章下面的标题似乎都有一个负的下边距,导致外观不一致。与你页面上其他标题的处理方式形成对比时,似乎有些不对劲。
由于你似乎非常关心网站的外观和感觉,我很好奇这是否是故意的设计决策,或者是否在某个阶段适合改进。
嗨,
我偶然发现了这些很酷的交互式 SVG 圣诞动画。
这是一个 Google Chrome 实验
请查看。
https://ihatetomatoes.net/svg-christmas/