在我跳槽到开发之前,我在 After Effects 中做了一些运动图形工作。但即使有这样的背景,我仍然觉得在网络上进行动画制作很令人费解。
视频图形是在特定比例内设计,然后导出。完成!但在网络上没有“导出设置”。我们只是将代码推送到世界,我们的动画必须适应它们所处的任何设备。
因此,让我们谈谈响应式动画!我们如何最好地处理在狂野的网络上进行动画制作?我们将介绍一些通用方法,一些特定于 GSAP 的技巧以及一些运动原则。让我们从一些框架开始......
此动画将如何使用?
Zach Saucier 的响应式动画文章 建议在跳入代码之前退一步思考最终结果。
动画将是重复出现在应用程序多个部分的模块吗?它需要扩展吗?牢记这一点可以帮助确定动画的扩展方法,并防止您浪费精力。
这是一个很好的建议。设计响应式动画的重要部分是了解动画是否以及如何需要扩展,然后从一开始就选择正确的方法。
大多数动画都属于以下类别
- 固定: 适用于图标或加载器等始终保持相同大小和纵横比的动画。这里没什么可担心的!在其中硬编码一些像素值,然后继续您的一天。
- 流畅: 需要在不同设备上流畅适应的动画。大多数布局动画都属于此类。
- 定向: 针对特定设备或屏幕尺寸的动画,或者在特定断点处发生重大变化,例如仅桌面动画或依赖于特定设备交互(如触摸或悬停)的交互。
流畅和定向动画需要不同的思维方式和解决方案。让我们看看......
流畅动画
正如 Andy Bell 所说:成为浏览器的导师,而不是它的微观管理者——给浏览器一些可靠的规则和提示,然后让它为访问它的人做出正确的决定。
(以下是 该演示文稿的幻灯片。)
流畅动画完全是关于让浏览器完成繁重的工作。许多动画可以轻松地通过从一开始就使用正确的单位来适应不同的上下文。如果您调整此笔的尺寸,您可以看到使用 视窗单位 的动画随着浏览器调整而流畅地缩放
紫色框甚至在不同的断点处改变宽度,但由于我们使用百分比来移动它,动画也会随之缩放。
为 left
和 top
等布局属性设置动画可能会导致布局重排和抖动“卡顿”动画,因此尽可能使用转换和不透明度。
我们不仅限于这些单位——让我们看看其他一些可能性。
SVG 单位
我喜欢使用 SVG 的一件事是我们可以使用 SVG 用户单位进行动画,这些单位开箱即用,具有响应性。提示就在名称中——可缩放矢量图形。在 SVG 世界中,所有元素都绘制在特定坐标上。SVG 空间就像一张无限大的方格纸,我们可以在此安排元素。viewBox
定义了我们可以看到的方格纸的尺寸。
viewBox="0 0 100 50”
在此下一个演示中,我们的 SVG viewBox
宽度为 100
个单位,高度为 50
个单位。这意味着如果我们沿 x 轴将元素动画 100
个单位,它将始终移动其父 SVG 的整个宽度,无论该 SVG 的大小如何!调整演示的大小以查看。
在 HTML 世界中,根据父容器的宽度为子元素设置动画要复杂一些。到目前为止,我们不得不使用 JavaScript 获取父容器的宽度,这在您从转换后的位置设置动画时很容易,但在您从某个位置设置动画时,就会变得有点棘手,如以下演示所示。如果您的终点是转换后的位置,并且您调整了屏幕大小,则必须手动调整该位置。很乱…… 🤔
如果您确实在调整大小后调整了值,请记住要 去抖,甚至在浏览器完成调整大小后才触发该函数。调整大小侦听器每秒都会触发大量事件,因此在每次事件上更新属性会给浏览器带来很大的工作量。
但是,这种动画速度障碍很快就会成为过去!请敲响鼓声...... 🥁
容器单位!太棒了。在我写这篇文章的时候,它们只在 Chrome 和 Safari 中工作——但也许在你读到这篇文章的时候,我们也会有 Firefox。在下一个演示中查看它们在实际中的应用。看看那些小家伙的行动!这难道不令人兴奋吗,相对于父元素的动画!
此浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示浏览器在该版本及其更高版本中支持该功能。
桌面
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
105 | 110 | 否 | 105 | 16.0 |
移动设备/平板电脑
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
127 | 127 | 127 | 16.0 |
使用 FLIP 进行流畅的布局过渡
正如我们之前提到的,在 SVG 世界中,每个元素都整齐地放置在一个网格上,并且非常容易响应地移动。在 HTML 世界中,它要复杂得多。为了构建响应式布局,我们使用了一系列不同的定位方法和布局系统。在网络上进行动画的主要困难之一是,许多布局更改无法进行动画处理。也许某个元素需要从位置 relative
移动到 fixed
,或者弹性容器的某些子元素需要在视窗中平滑地重新排列。也许某个元素甚至需要重新设置父元素并移动到 DOM 中的完全不同位置。
棘手,对吧?
好吧。FLIP 技术可以拯救我们;它允许我们轻松地为这些不可能的事情设置动画。基本前提是
- 首先:获取参与过渡的元素的初始位置。
- 最后:移动元素并获取最终位置。
- 反转:计算出第一个状态和最后一个状态之间的变化,并应用转换以将元素反转回其原始位置。这使得它看起来像元素仍然处于第一个位置,但实际上并非如此。
- 播放:删除反转转换,并将其动画设置为其伪造的第一个状态到最后一个状态。
这是一个使用 GSAP 的 FLIP 插件的演示,它为您完成所有繁重的工作!
如果您想更多地了解原生实现,请访问 Paul Lewis 的 博客文章——他是 FLIP 技术背后的智囊。
流畅地缩放 SVG
你抓住了我……这不是真正的动画技巧。但是,正确设置舞台对于良好的动画至关重要!SVG 默认情况下可以很好地缩放,但我们可以使用 preserveAspectRatio
来进一步控制其缩放方式,这在 SVG 元素的纵横比和 viewBox
的纵横比不同时非常有用。它的工作原理与 CSS 中的 background-position
和 background-size
属性非常相似。声明由对齐值(background-position
)和Meet 或Slice 参考(background-size
)组成。
对于 Meet 和 Slice 参考——slice
就像 background size: cover
,meet
就像 background-size: contain
。
preserveAspectRatio="MidYMax slice"
— 对齐到 x 轴的中间,y 轴的底部,并放大以覆盖整个视口。preserveAspectRatio="MinYMin meet"
— 对齐到 x 轴的左侧,y 轴的顶部,并在保持整个viewBox
可见的情况下放大。
Tom Miller 通过在 CSS 中使用 overflow: visible
和一个包含元素来进一步实现这一点,从而在保持高度限制的情况下显示“舞台左侧”和“舞台右侧”。
对于响应式 SVG 动画,利用 SVG 视口创建在特定浏览器宽度下裁剪和缩放的视图,同时在浏览器宽度大于该阈值时向左右显示更多 SVG 动画,会非常有用。我们可以通过在 SVG 上添加 overflow visible 并将其与
max-height
包装器结合使用来实现这一点,以防止 SVG 在垂直方向上过分缩放。
流畅缩放画布
对于具有大量运动部件的复杂动画,Canvas 的性能远高于 SVG 或 HTML DOM 动画,但它本身也更复杂。你必须为这些性能提升付出努力!与 SVG 不同,SVG 有很棒的响应式单位和开箱即用的缩放功能,<canvas>
需要被控制和微管理。
我喜欢将我的 <canvas>
设置为与 SVG 非常相似的方式(我可能会有偏见),使用很棒的单位系统在其中工作并保持固定的纵横比。<canvas>
也需要在每次发生更改时重新绘制,因此请记住将重新绘制延迟到浏览器完成调整大小后,或者使用防抖动!
George Francis 还制作了这个 很棒的小型库,它允许你定义 Canvas viewBox
属性和 preserveAspectRatio
— 就像 SVG 一样!
目标动画
有时你可能需要采取更直接的方式来进行动画。移动设备的空间更小,而且动画性能比台式机差。因此,向移动用户提供缩减的动画是有意义的,甚至可能是不提供动画。
有时,最适合移动端的响应式动画就是完全不使用动画!对于移动端的 UX,优先让用户快速浏览内容,而不是等待动画完成。移动端的动画应该增强内容、导航和交互,而不是延迟它们。Eric van Holtz
为了做到这一点,我们可以像在使用 CSS 样式时一样,利用媒体查询来定位特定的视口大小!这是一个简单的演示,展示了使用媒体查询处理 CSS 动画,以及使用 gsap.matchMedia()
处理 GSAP 动画。
这个演示的简单性隐藏了大量的魔法!为了使 JavaScript 动画仅在特定屏幕尺寸下正常工作,需要进行更多设置和清理。我过去见过一些恐怖的例子,人们只是用 opacity: 0
在 CSS 中隐藏了动画,但动画仍在后台消耗资源。😱
如果屏幕尺寸不再匹配,则需要终止动画并释放它以进行垃圾回收,并且需要清除受动画影响的元素的任何由运动引入的内联样式,以防止与其他样式冲突。在 gsap.matchMedia()
之前,这是一个棘手的过程。我们必须跟踪每个动画并手动管理所有这些。
gsap.matchMedia()
使你能够轻松地将动画代码放入一个仅在特定 媒体查询 匹配时才执行的函数中。然后,当它不再匹配时,该函数中的所有 GSAP 动画和 ScrollTriggers 会自动恢复。动画所包含的媒体查询会为你完成所有繁重的工作。它包含在 GSAP 3.11.0 中,是一个改变游戏规则的功能!
我们也不仅仅局限于屏幕尺寸。有很多 媒体特性 可以使用!
(prefers-reduced-motion) /* find out if the user would prefer less animation */
(orientation: portrait) /* check the user's device orientation */
(max-resolution: 300dpi) /* check the pixel density of the device */
在以下演示中,我们添加了对 prefers-reduced-motion
的检查,因此任何觉得动画令人迷惑的用户都不会受到周围事物飞来飞去的影响。
还可以查看 Tom Miller 的另一个有趣的演示,他使用设备的纵横比来调整动画。
跳出思维定势,超越屏幕尺寸
响应式动画的思考范围不仅仅是屏幕尺寸。不同的设备支持不同的交互方式,如果不考虑这一点,很容易陷入混乱。如果你在 CSS 中创建悬停状态,可以使用 hover
媒体特性来测试用户的主要输入机制是否可以悬停在元素上。
@media (hover: hover) {
/* CSS hover state here */
}
来自 Jake Whiteley 的一些建议:
很多时候,我们会根据浏览器宽度来设计动画,天真地认为台式机用户希望使用悬停状态。我个人过去遇到过很多问题,例如当切换到台式机布局(>1024px)时,可能会在 JS 中进行触摸检测,导致布局是为台式机设计的,但 JS 代码是为移动设备设计的。现在我更倾向于使用 hover 和 pointer 来确保一致性,并处理 iPad Pro 或 Windows Surface 等设备(它们可以根据盖子是否关闭来更改指针类型)。
/* any touch device: */
(hover: none) and (pointer: coarse)
/* iPad Pro */
(hover: none) and (pointer: coarse) and (min-width: 1024px)
然后我会将我的 CSS 布局查询和 JavaScript 查询结合起来,以便将输入设备作为支持宽度的主要因素,而不是相反。
ScrollTrigger 提示
如果你使用 GSAP 的 ScrollTrigger 插件,则可以使用一个方便的小工具来轻松识别设备的触摸功能:ScrollTrigger.isTouch
。
0
– 无触摸(仅指针/鼠标)1
– 仅触摸设备(如手机)2
– 设备可以接受触摸输入和鼠标/指针(如 Windows 平板电脑)
if (ScrollTrigger.isTouch) {
// any touch-capable device...
}
// or get more specific:
if (ScrollTrigger.isTouch === 1) {
// touch-only device
}
关于响应式滚动触发动画的另一个提示...
下面的演示正在水平移动一个图像库,但宽度根据屏幕尺寸而变化。如果你在滚动动画进行到一半时调整屏幕大小,可能会导致动画中断和值过时。这是一个常见的障碍,但很容易解决!将依赖于屏幕尺寸的计算放入一个函数值中,并设置 invalidateOnRefresh:true
。这样,ScrollTrigger 就会在浏览器调整大小后重新计算该值。
GSAP 高级提示!
在移动设备上,浏览器地址栏通常会在滚动时显示和隐藏。这算作调整大小事件,会触发 ScrollTrigger.refresh()
。这可能并不理想,因为它会导致动画跳动。GSAP 3.10 添加了 ignoreMobileResize
。它不会影响浏览器栏的行为方式,但会阻止 ScrollTrigger.refresh()
在仅触摸设备上发生较小的垂直调整大小时触发。
ScrollTrigger.config({
ignoreMobileResize: true
});
运动原理
我想留下一些在网页上使用运动时需要考虑的最佳实践。
距离和缓动
一个容易被忽视的小细节,但对于响应式动画来说很重要,那就是速度、动量和距离之间的关系!好的动画 应该模仿现实世界,让人感觉可信,在现实世界中,覆盖更长的距离需要更长的时间。注意动画的移动距离,并确保所使用的持续时间和缓动在与其他动画的上下文中是合理的。
你还可以使用更强烈的缓动效果来显示移动更远距离的元素的更大动量。
在某些情况下,根据屏幕宽度动态调整持续时间可能会有帮助。在下一个演示中,我们使用了 gsap.utils
将从当前 window.innerWidth
获取的值钳定到合理的范围内,然后将该值映射到持续时间。
间距和数量
另外需要注意的是,在不同屏幕尺寸下元素的间距和数量。引用 Steven Shaw 的话
如果你在窗口周围放置了一些环境动画(视差、云、树木、彩带、装饰品等),请确保它们根据屏幕尺寸进行缩放和/或调整数量。大屏幕可能需要更多元素分布在整个屏幕上,而小屏幕只需要几个元素来达到相同的效果。
我喜欢 Opher Vishnia 将动画视为舞台的思考方式。添加和移除元素不仅仅是一种形式,它可以成为整体编排的一部分。
在设计响应式动画时,挑战不在于如何将相同的内容塞入视窗使其“适合”,而在于如何策划现有内容集,使其传达相同的意图。这意味着要做出有意识的选择,决定添加哪些内容,删除哪些内容。通常在动画的世界里,事物不会简单地跳入或跳出画面。将元素视为进入或退出“舞台”是有意义的,以一种在视觉和主题上都有意义的方式为这种过渡制作动画。
这就是全部内容。如果你还有其他响应式动画技巧,请在评论区分享。如果有什么特别有用的技巧,我会将它们添加到这个信息汇编中!
附录
当我准备这篇文章时,Tom Miller 又补充了一点
对于你的响应式动画文章,我可能已经晚了,但我强烈建议“在构建之前完成所有动画”。我目前正在为一些网站动画添加“移动版本”。感谢
gsap.matchMedia
的存在……但我真希望我们一开始就知道会有单独的移动布局/动画。
我想我们都感谢这个“提前计划”的建议是在最后时刻出现的。谢谢,Tom,祝你的改造一切顺利。
谢谢,这是一篇非常有用的文章。我实际上刚开始接触动画和学习相关代码,一边摸索一边学习(非常缓慢)。在同一篇文章中获得如此有帮助的定制/精选信息非常有用。
啊,听到这个消息真好,Lee,感谢你的评论。如果你遇到问题或有任何疑问,请随时联系我,我很乐意提供帮助。✨
看看 Wayfinder 动画工具。它允许你将元素像路标一样对待——你可以响应式地设计你的网站,然后无论用户使用什么设备/屏幕尺寸,都可以对任何元素进行动画。
它目前仍处于测试阶段,我正在完善网站的文档页面。你可以将其与任何 JS/TS 动画库一起使用。
https://github.com/anxpara/wayfinder-animation-tool
-anx
不可思议的作品!感谢你分享这些宝贵的知识。