高性能的视差动画【译】
高性能的视差动画
爱也好,恨也好,视差效果已经遍布web之上了。当你用的巧妙的时候,它可以给应用增加深度和隐喻效果。但问题在于实现一个高性能的视差效果是一个很有挑战的工作。在这篇文章里,我们将讨论如何构造一个高性能的视差效果,当然同样重要的是还得跨浏览器。
视差效果示意图
摘要
不要使用滚动事件(scroll events)或者背景定位(background-position)来创建视差动画。
使用 CSS 3D 变换来创建一个更准确的视差效果。
对于iOS移动设备的Safari浏览器使用
position: sticky
来确保视差可以生效。
如果你想要一个开箱即用的方案,请访问 Parallax helper JS ,这里还有一个 demo演示。
视差的问题分析
在开始之前,我们先来看两个现有的常见的实现视差的方法,探讨为何它们不适合我们要追求的目标。
不好的方案:使用滚动事件
视差的关键需求是它应该是滚动耦合的:对于页面滚动的每一个位置变化,视差元素的位置也应被更新。这看上去很容易,现代浏览器的重要机制之一就是它们可以异步处理工作。尽管如此,在大多数浏览器中,滚动事件是被作为“尽量好”(best effort)的工作处理的,也就意味着:浏览器并不确保每一帧的滚动动画送达!
这个重要的信息告诉我们为什么要避免使用Javascript基于滚动事件去移动元素:Javascript并不能确保视差会和页面滚动保持同样的步调。。在一些老版本的Mobile Safari上,滚动事件甚至是在滚动完成后才触发的,这一点让基于Javascript的滚动效果无法实现。在较新的Mobile Safari版本中,滚动事件可以在动画过程中触发了,但是和Chrome一样,它是一个基于“尽量好”的原则的。所以当主线程忙于其他工作时,滚动事件不会立即触发,也就意味着视差效果的丢失。
不好的方案:更新背景位置
我们要避免的另一个场景是在每帧都进行绘制。很多方案试图采用改变 background-position
来提供视差效果, 但这会让浏览器在滚动时重绘受影响的部分,而这可能会是相当消耗资源的,这种影响会使动画卡顿。
如果我们想提供一个高质量的视差动画,我们想要的是一个可以当作加速的属性(这里我们指的是 transform
和 opacity
),而这是不依赖于滚动事件的。
CSS 3D
Scott Kellum 和 Keith Clark 都已经在利用 CSS 3D 来实现视差效果领域做出了很重要的工作。他们采用的非常有效技术有:
建立一个容器元素,设置
<a href="http://www.php.cn/wiki/926.html" target="_blank">overflow-y</a>: scroll
使其可以滚动(同时可能需要<a href="http://www.php.cn/wiki/924.html" target="_blank">overflow-x</a>: hidden
)。对于上面的元素, 我们会应用一个
perspective
值,然后设置perspective-origin
到<a href="http://www.php.cn/wiki/904.html" target="_blank">top</a> left
, 或者0 0
。对上面元素的子元素应用一个在 Z 轴的变换,然后把它们还原回来以实现视差效果,而没有影响它们在屏幕上的大小。
这种方案的 CSS 看起来是下面的样子:
.container { width: 100%; height: 100%; overflow-x: hidden; overflow-y: scroll; perspective: 1px; perspective-origin: 0 0; } .parallax-child { transform-origin: 0 0; transform: translateZ(-2px) scale(3); }
当然我们假定你的 HTML 是下面的样子:
<p class="container”> <p class="parallax-child”></p> </p>
调整perspective的比例
把子元素挤回来会要求它设置一个更小的相对于 perspective
的比例。你可以通过下列等式来计算需要的缩放比例: (perspective - distance) / perspective。由于我们希望视差元素看上去和我们一开始设定的一样大,所以它应该根据这样的等式进行放缩而不是保持不变。
拿上面的例子来说, perspective
是 1px
, 而 parallax-child
在 Z 轴方向是 -2px
,这就意味着元素需要被放大3倍,你可以看到我们在 scale
中写入了 3
这个值。
对于任何没有应用 translateZ
的内容,你可以用 0 替代,也就是缩放比为 (perspective - 0) / perspective
,结果为 1 ,即既不放大也不缩小。真的是非常方便。
为什么这种方案好用?
弄清楚为什么这种方案好用是非常重要的,因为我们很快就要使用这个知识了。滚动其实本质是一种变换,这是它为什么可以被加速的原因。滚动很大程度上使用GPU参与了图层的变换。一个常见的滚动(没有应用任何 perspective
)是这样的:滚动这种情况下是以 1:1
的方式在对待滚动的元素和它的子元素。换人话说,如果你向下滚动一个元素 <a href="http://www.php.cn/code/4221.html" target="_blank">300</a>px
,那么它的子元素向上变换了同样的数量: 300px
。
但是,如果对这个滚动元素应用 perspective
值会把这个过程“搞乱”:这个值改变了滚动变换的理想路线。现在如果一个 300px
的滚动可能把子元素移动了 150px
,当然这取决于你给 perspective
和 translateZ
设置什么值。如果一个 translateZ
设置为0的子元素,它的滚动会一切如常 ( 1:1
),但是一个被推向 Z 轴向( translateZ
不为 0 )的子元素将以不同的比例滚动!因此出现了视差效果。另外非常重要的一点是:这个过程本身就是浏览器内部的滚动机制的一部分,因此没有必要监听滚动事件或者改变背景位置。
美中不足:Mobile Safari
每种效果都有一些约束,对于变换( transform
)来讲,对子元素的 3D 效果的保持就是这样。如果在应用 perspective
的元素和它的“视差”子元素的结构之中有其它元素的存在,那么 3D 的效果会被“拍扁”,也就是说效果将不复存在。
<p class="container”> <p class="parallax-container”> <p class="parallax-child”></p> </p> </p>
在上面的HTML中 .parallax-container
是一个新添加的元素,而它会"抹平" perspective
,从而导致效果丢失。通常情况下,解决方案还是比较符合直觉的:给这个元素添加 transform-style: preserve-3d
以便让它可以把 3D 效果应用到更深层的节点去。
.parallax-container { transform-style: preserve-3d; }
对于 Mobile Safari 来说,事情变的有点麻烦。对容器元素应用 overflow-y
: 技术上这是没问题的,但是这会让滚动元素的移动过于凶猛。解决方案是加上一个 -webkit-overflow-scrolling: touch
,但这个设置会导致 perspective
被抹平,因此我们会得不到任何视差效果。
从一个发展的角度看,这可能算不上什么问题(因为只在旧版本的 Mobile Safari 出现),即使我们无法在每一个浏览器中展现视差效果,但一个应用的功能还是好用的,但我们最好找出一个方案。
position:sticky
来拯救
事实上,我们可以从 position: sticky
中得到一些帮助,这个设置允许元素固定在 <a href="http://www.php.cn/css/css-rwd-viewport.html" target="_blank">viewport</a>
的顶部或者固定在一个滚动元素的父元素。这个属性的文档,就如同任何其它文档一样,又臭又长,但是还是可以找到一些有用信息:
一个固定的“盒子”非常像一个相对定位的盒子,但是位移是通过引用最近的可滚动的祖先来计算的,或者根据
viewport
来计算,如果找不到这样一个祖先的话 -- CSS Positioned Layout Module Level 3
第一眼看上去好像帮助不大,但一个关键点在句中说到如何计算元素的固定位置的那部分:“位移是通过引用最近的可滚动的祖先来计算的”。换句话说,移动固定元素的距离(为了让它看起来是固定在某个元素或者 viewport
上)是在应用其它任何变换之前进行计算的,而不是之后。这就意味着,和我们刚才说的滚动的那个例子很像,如果位移计算的结果是 300px
,那么我们就有了一个新的机会去使用 perspective
(或者其它任何变换)来在这个 300px
应用到固定元素之前去改变它。
通过给视差元素设置 position: -webkit-sticky
,我们可以有效的“翻转”那个由于 -webkit-overflow-scrolling: touch
而产生的“抹平”效果。这样就确保了视差元素引用最近的可滚动的祖先元素,这里就是 .container
。然后,和上文讲的类似,给 .parallax-container
设置一个 perspective
值,这样就改变了计算的滚动位移,创建出了视差效果。
<p class="container”> <p class="parallax-container”> <p class="parallax-child”></p> </p> </p> .container { overflow-y: scroll; -webkit-overflow-scrolling: touch; } .parallax-container { perspective: 1px; } .parallax-child { position: -webkit-sticky; top: 0px; transform: translate(-2px) scale(3); }
这样就为 Mobile Safari 恢复了视差效果,真是一个不错的结果。
固定定位的问题
和之前的方案确实还有一个明显区别, position: sticky
改变了视差的机制。固定定位试图固定某个元素在滚动容器顶端,而非固定元素不是。这就意味着固定定位产生的视差和非固定产生的色差是相反的:
使用
position: sticky
: 元素离z=0
越近,它移动的越少不使用
position: sticky
: 元素离z=0
越近,它移动的越多
如果你还是感到有些抽象的话,可以看一下Robert Flack的这个demo,这个demo展示了在固定定位和非固定定位的条件下,元素是如何有不同的表现的。要看到这个效果的话,你需要 Chrome Canary (写作本文是,版本为56) 或者 Safari 。
position: sticky
对视差的影响
花式bug和应对
如同任何事情一样,还是有很多的坑需要填。
固定定位的支持是不一致的:当前在 Chrome 中对于这个特性还在开发中,Edge 则完全缺失,FireFox则有绘制的bug。在这种情况下,我们应该增加一点代码来在需要时(这里就是 Mobile Safari )才添加
position: sticky
该效果在 Edge 中完全没有作用。Edge试图在OS级别处理滚动,通常情况下这是个好事。但在这个例子中,这种机制会使得我们无法在滚动时去应用
perspective
。为了修复这个问题,我们可以为父元素 设置 `translateZ(0px)``。页面内容太大了:在决定页面内容有多大时,很多浏览器负责放缩,但很遗憾 Chrome 和 Safari 不负责。所以假如有一个放大 3 倍的变换应用到某个元素时,你可能会看到滚动条出现了,而且一旦出现后,即使之后你恢复了
1:1
的比例,滚动条也不会消失。有一个方法可以避免这种情况:那就是从右下角进行放缩(transform-origin: <a href="http://www.php.cn/wiki/906.html" target="_blank">bottom</a> <a href="http://www.php.cn/wiki/905.html" target="_blank">right</a>
)。这种方案背后的原理是它会导致过大的元素进入滚动区域的“负面”(一般是左上),而滚动区域永远不会让你看到“负面”区域。
结论
视差动画如果经过的周全的设计考虑后会是一个非常有趣的效果。而且现在你应该可以了解到我们是可以实现一个高性能的、滚动耦合的、跨浏览器的方案。由于这里面需要一点点数学计算和一些模板化的操作,所以我们封装了一个工具类和例子。
欢迎试用,并提出您的宝贵意见。
以上是高性能的视差动画【译】的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

如何使用Swoole实现高性能的HTTP反向代理服务器Swoole是一款基于PHP语言的高性能、异步、并发的网络通信框架。它提供了一系列的网络功能,可以用于实现HTTP服务器、WebSocket服务器等。在本文中,我们将介绍如何使用Swoole来实现一个高性能的HTTP反向代理服务器,并提供具体的代码示例。环境配置首先,我们需要在服务器上安装Swoole扩展

PHP和WebSocket:构建高性能的实时应用程序随着互联网的发展和用户需求的提升,实时应用程序变得越来越普遍。而传统的HTTP协议在处理实时数据时存在一些限制,比如需要频繁的轮询或长轮询方式来获取最新的数据。为了解决这个问题,WebSocket应运而生。WebSocket是一种先进的通信协议,它提供了双向通信的能力,允许浏览器和服务器之间实时地发送和接

C++是一种高性能的编程语言,可以为开发人员提供灵活性和可扩展性。尤其在大规模数据处理场景下,C++的高效和快速运算速度是非常重要的。本文将介绍一些优化C++代码的技巧,以应对大规模数据处理需求。使用STL容器代替传统数组在C++编程中,数组是常用的数据结构之一。但是,在大规模数据处理中,使用STL容器,如vector,deque,list和set等,可以更

随着科技的不断发展,语音识别技术也得到了长足的进步和应用。语音识别应用广泛运用在语音助手、智能音箱、虚拟现实等领域,为人们提供了更加便捷和智能的交互方式。而如何实现高性能的语音识别应用,则成为了一个值得探讨的问题。近年来,Go语言作为一种高性能的编程语言,在语音识别应用的开发中备受瞩目。Go语言具备并发性高、编写简洁、执行速度快等特点,非常适合用于构建高性能

使用Go语言开发实现高性能的人脸识别应用摘要:人脸识别技术是一个在当今互联网时代非常流行的应用领域。本文介绍了使用Go语言开发高性能人脸识别应用的步骤和流程。通过使用Go语言的并发、高性能和简单易用的特点,开发人员可以更加便捷地构建出高性能的人脸识别应用。引言:在当今的信息化社会,人脸识别技术被广泛应用于安全监控、人脸支付、人脸解锁等领域。随着互联网的快速发

Java开发:如何使用Netty进行高性能网络编程摘要:Netty是一个高性能、异步事件驱动的网络编程框架,能够简化网络应用程序的开发过程。本文将介绍Netty的主要特点以及如何使用Netty进行高性能网络编程。同时,我们还会提供一些具体的Java代码示例,帮助读者更好地理解和应用Netty。一、Netty简介Netty是一个基于JavaNIO的网络编程框

Docker和SpringBoot的技术实践:快速搭建高性能的应用服务引言:在当今的信息时代,互联网应用的开发和部署变得越来越重要。随着云计算和虚拟化技术的快速发展,Docker作为一个轻量级的容器技术,受到了广泛关注和应用。而SpringBoot作为一种快速开发和部署Java应用的框架,也得到了广泛认可。本文将探讨如何结合Docker和SpringB

高性能数据库搜索算法的Java实现思路摘要:随着互联网和大数据时代的到来,数据库的存储和搜索性能对于数据处理的效率至关重要。本文将介绍一种高性能数据库搜索算法的Java实现思路,并提供具体的代码示例。引言数据库搜索是对于大规模数据集合中的快速查询的关键操作之一。传统的数据库搜索算法存在搜索效率低下的问题,无法满足大数据时代的需求。因此,高性能数据库搜索算法的
