视差滚动实践
视差滚动(Parallax Scrolling)是指让多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验。官网的设计师们也十分热衷于这样的动效,最近的新品页面或多或少都运用了视差滚动效果。起初官网视差基本上是使用插件去完成简单的效果,但时代在进步,设计师的要求也越来越高,要实现的效果也越来越复杂,对页面性能也带来巨大负担。实现高效的视差滚动成了新的挑战。
视差滚动的原理
实现视差滚动,主要是对页面上的元素进行分层,让其以不同于页面滚轮滚动的速度运动,这样看上去就形成了视觉上的差异。
通常来说可以把页面上的元素分成三个层次:背景层,前景层(内容层和背景层之间的元素),内容层。从这三个层次入手,就能营造出视差的效果:
(1)背景层的滚动(最慢);
(2)前景层的滚动(次慢);
(3)内容层的滚动(可以和页面的滚动速度一致)。
视差滚动实现
简单实现 😶
设置元素的背景属性 background-attachment
为 fixed
。默认情况下,此属性取值为scroll,页面滚动时,内容和背景一起运动,如果取值fixed,背景相对浏览器固定。这样看上去背景和内容就分开了滚动了。
例如ronin-s页面。
但是这样的效果,设计师一定会说:太死板了吧,能不能有点惯性的感觉?
于是乎,我们要让页面元素真正的“动起来”。
动起来 🙂
我们开始监听用户滚动事件,对于前景层的内容,可以随着用户滚动去改变元素的 translate(x, y, z);对于背景层则去改变 background-position。
例如mavic-air页面就是通过改变元素背景位置,使背景图片”动起来“的。
代码如下:
网上也有许多视差滚动的插件,可以直接使用 😀
parallax.js
Stella.js
Super Scrollorama
curtain.js
好处
相比于 background-attachment: fixed
方式,我们可以随心所欲的定义元素的动画,让页面看起来更生动,动画更平滑。
带来的问题
监听滚动事件,要想做到尽可能地流畅渲染效果,就不可以让滚动事件节流防抖动,必须要时刻紧跟滚动事件才行,显然是有些耗费性能的。改变一个非绝对定位元素的位置,是很有可能会触发页面的重绘,而改变 background-position 同样是会出现这种情况,如果每一帧都渲染,显然非常耗费性能,如果页面功能复杂甚至可能造成页面的卡顿。
高性能 😆
为了提高性能,尝试了一种新的方式:css 3D Transforms
使用css实现视差滚动效果可以解决上述这些问题,并允许浏览器利用硬件加速,实现帧速相同的平滑滚动。
实现方式如下:
|
|
- 将滚动的容器元素属性设置为overflow-y: scroll(和overflow-x:hidden)。
- 对容器元素元素应用perspective值,并将perspective-origin设置为top left或0 0。
- 对容器元素的子元素应用Z轴变形,通过缩放子元素实现视差效果。
运用这个方式实现了一个demo
实现原理
定义滚动容器元素的 perspective
属性将创建固定的透视图3D视口。设置 overflow-y:auto
使元素的内容以正常的方式滚动,但后代元素将相对于透视图呈现,这是创建视差效果的关键。对子元素设置translateZ属性,将其移动更远或更靠近视口,在Z轴上远离视口的子元素会以不同的比率滚动,这样就产生了视差滚动。非常重要的是,这一过程作为浏览器内部滚动机制一部分自动处理,无需监听滚动事件或改变背景位置。
由于使用3D变换创建了视差效应,因此对于沿着Z轴转换的元素具有副作用——当我们将其移动距离视口更近或更远时,其可视大小会发生变化。为了解决这个问题,我们需要对该元素应用一个scale变换,使其看起来以原始大小呈现。
scale可以用1 +(translateZ * -1)/perspective
来计算。例如,如果我们的视口perspective为1px,并且我们沿Z轴translateZ (-2px),则校正的scale值将为3。
网上有个demo可以让你直观的理解这个原理。
兼容性问题
关于 3D Transforms,目前主流浏览器都可以支持(具体情况参考caniuse),对于不支持的浏览器只能做降级处理。
结语
视差滚动其实是个非常有趣的特效。运用得当可以让我们的网页体验更上一层~