纯CSS实现视差滚动

纯CSS实现视差滚动

利用CSS transforms perspective 和 scale 来创造纯CSS的视差滚动.

Parallax几乎总是使用JavaScript来处理,而且通常情况下,最糟糕的做法是监听滚动事件并直接在处理程序中修改DOM,从而触发不必要的repaint和reflow。,虽然可以通过requestAnimationFrame延迟DOM更新做出视差效果,这些异步操作导致浏览器丢帧。

使用css实现视差滚动效果可以解决上述这些问题,并允许浏览器利用硬件加速,实现帧速相同的平滑滚动。还可以将效果与其他CSS功能相结合,如媒体查询。

尝试demo

理论:

1
2
3
4
5
6
<div class="parallax">
<div class="parallax-layer parallax-layer_base logo"> XINRAN LIU </div>
<div class="parallax-layer parallax-layer_back">
...
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.parallax{
perspective: 1px
height: 100vh
overflow-x: hidden
overflow-y: auto
}
.parallax-layer{
position: absolute
top: 0
right: 0
bottom: 0
left: 0
}
.parallax-layer_base{
transform translateZ(-1px)
}
.parallax-layer_back{
transform translateZ(0)
}

parallax类是视差魔术发生的地方。定义元素的heightperspective属性将创建固定的透视图3D视口。设置overflow-y:auto使元素的内容以正常的方式滚动,但后代元素将相对于透视图呈现,这是创建视差效果的关键。

接下来是parallax__layer类。顾名思义,它定义了将应用视差效应的内容层。

最后,我们有parallax__layer--baseparallax__layer--back。这些用于通过沿着Z轴(将其移动更远或更靠近视口)来确定视差元件的滚动速度。为了简洁起见,我只定义了两层。

demo1

大小调整

由于使用3D变换创建了视差效应,因此对于沿着Z轴转换的元素具有副作用——当我们将其移动距离视口更近或更远时,其可视大小会发生变化。为了解决这个问题,我们需要对该元素应用一个scale变换,使其看起来以原始大小呈现:

perspective

个人的理解的是perspective是模拟眼睛到屏幕的距离.

  • 没有perspective就不是真3D
  • 当perpective的值越小,3D效果就越明显(就是说你眼睛越3靠近3D就越3D)
    translateZ的功能就是让元素在自己的眼前或近或远。
1
2
3
.parallax__layer--back {
transform: translateZ(-1px) scale(2);
}

scale可以用1 +(translateZ * -1)/perspective来计算。例如,如果我们的视口perspective为1px,并且我们沿Z轴translateZ (-2px),则校正的scale值将为3

1
2
3
.parallax__layer--deep {
transform: translateZ(-2px) scale(3);
}

控制速度

其实到这里整个原理就比较清晰了:当屏幕移动的时候,离屏幕远的元素看起来移动得比较慢,而离屏幕近的元素移动看起来就会比较快.

因此我们可以根据这个原理来控制元素移动的速度了.通过perspectivetranslate Z的组合来控制。具有负Z值的元素将比具有正值的元素滚动慢。

然后再给不同元素分别加上不同的 transform属性,translateZ 值调节元素在 Z 轴的位置(近大远小),同时配合 scale 值让元素的大小看起来和原来无异。那么就实现了滚动过程中,不同元素看起来的运动速度不同。

1
2
3
4
5
6
7
8
9
.img-1 {
transform: translateZ(-1px) scale(2); //变慢两倍
}
.img-2 {
transform: translateZ(-2px) scale(3); //变慢三倍
}
.text-1 {
transform: translateZ(0.5px) scale(0.5); //变快两倍
}

这里有一个设置scale值的公式:1 + (translateZ * -1) / perspective)

1
2
3
.parallax__layer--deep {
transform: translateZ(-2px) scale(3);
}

另外要注意的几点是:

  • 目前浏览器都不支持 perspective 属性。Chrome 和 Safari 支持替代的 -webkit-perspective 属性。
  • perspective 属性定义 3D 元素距视图的距离,以像素计。
  • 当为元素定义 perspective 属性时,其子元素会获得透视效果,而不是元素本身。
    看到设置了以上CSS之后首页变成了这个样子

由于一些老的浏览器不支持3d transfrom,因此应该把视差效果作为一种渐进增强,可以使用 @support来检测浏览器是否支持此效果

1
2
3
@supports ((perspective: 1px) and (not (-webkit-overflow-scrolling: touch))) {
/* ... parallax styles ... */
}

纯CSS视差滚动 在 Chrome 28+, Firefox 22+, Safari 9+ (OSX only), Opera 12+ 和 Edge都是可用的。

当然,如果你不觉得视差网站在小屏幕上运行良好,也可以将@supports块嵌套在@media查询中以禁用小屏幕的效果

1
2
3
4
5
@media screen and (min-width: 40em) {
@supports ((perspective: 1px) and (not (-webkit-overflow-scrolling: touch))) {
/* ... parallax styles ... */
}
}

特征检测demo

视差部分

之前的例子层次比较简单,但大多数视差站点将页面分成不同的部分,可以应用不同的效果。这是怎么做到的

首先,我们需要一个parallax__group元素来将我们的图层组合在一起:

1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="parallax">
<div class="parallax__group">
<div class="parallax__layer parallax__layer--back">
...
</div>
<div class="parallax__layer parallax__layer--base">
...
</div>
</div>
<div class="parallax__group">
...
</div>
</div>
1
2
3
4
5
.parallax__group {
position: relative;
height: 100vh;
transform-style: preserve-3d;
}

在这个例子中,我希望每个视差group填充整个视口,所以我设置height:100vh,但是如果需要,可以为每个组设置任意值。 transform-style:preserve-3d可以防止浏览器平铺parallax__layer元素和position:relative用于子parallax__layer元素相对于视差组元素定位。

分组元素时要牢记的一个重要规则是,我们无法裁剪组的内容。将视差组元素设置为overflow: hidden会破坏视差效果。未裁剪的内容将导致后代元素溢出,因此我们需要使用视差组的z-index值,以确保内容正确显示/隐藏

处理分层没有什么简单快速的方法,因为不同的设计的实现会有所不同。 如果你可以看到视差效应如何工作,调试分层问题要容易得多,这可以通过将简单的transform变换应用于组元素来实现:

1
2
3
.parallax__group {
     transform:translate3d(700px,0,-800px)rotateY(30deg);
}

看看下面的例子 - 注意debug选项!

demo for debug

相关文章:

原文Pure CSS parallax scrolling websites

坚持原创技术分享,您的支持将鼓励我继续创作!