纯CSS实现背景滚动视差效果-兼容移动端

前言

滚动视差效果应该都不陌生,一种常见的方法是用JS,监听滚动事件,在滚动时不断计算并设置容器的background-position。优点是兼容老平台IE,html结构简单,缺点是性能开销太大,以至于移动端兼容极差。

如何兼容移动端?

其实通过简单地设置background-attachment: fixed;也能实现背景不动的“视差”效果,视觉效果也比较强。但是!这个移动端也不兼容!!!(除了chrome等极少数)。这个老早之前由于开销太大而被砍掉的功能,都2020年了还不给加回来……

好在移动端大多数浏览器还是支持translate3d的,可以直接用CSS实现,性能更高也更自然。

好像iOS还是不支持,至少我那个做出来用iPhone 7 Plus测试是没有效果的。

效果

这里直接上效果图

建议直接看demo页面

可以看到,背景滚动的速度是明显比内容慢的,有多个背景。

原理及实现

先上代码

<div class="parallax-stage">
    <div class="main">
        <div class="row" id="row0">
            <div class="bg-container">
                <!-- 背景元素 -->
                <div class="background"></div>
            </div>
            <div class="content-dark">
                content here...
            </div>
        </div>

        <div class="row" id="row1">
            <!-- #row1没有背景元素,显示为白色 -->
            <div class="content-light">
                <!-- 此处为内容 -->
                ...
            </div>
        </div>

        <!-- #row2与#row0相同 -->
        <div class="row" id="row2">
            <div class="bg-container">
                <div class="background"></div>
            </div>
            <div class="content-dark">
                <!-- 此处为内容 -->
                ...
            </div>
        </div>

        <!-- 其他的 row 省略 -->
        <div class="row" id="row3">
            ...
        </div>
        ...
    </div>
</div> 
.body {
    margin: 0; /* 确保沉浸效果 */
}

.parallax-stage { 
    /* 滚动容器 */
    height: 100vh;
    width: 100%;
        /*主要看下面的*/
    perspective: 1px;
    overflow: auto;
}

.main {
    position: relative;
    /* 这里也需要3D视角,否则无法兼容Firefox等 */
    transform-style: preserve-3d;
}

.row {
    position: relative;
    overflow: hidden;
}

.bg-container {
    height: 100%;
    width: 100%;
    /*主要看下面的*/
    /* 视差元素的父级需要3D视角 */
    position: absolute;
    transform-style: preserve-3d;
}

.background {
    /* 滚动比较慢的背景元素 */
    width: 100%;
    background-size: cover;
    background-position: center;
    /*主要看下面的*/
    position: absolute;
        /* 这个-2vw和3.05后面单独讲 */
    transform: translate3d(-2vw, -100vh, -2px) scale(3.05);
    transform-origin: top;
    height: calc((100% - 100vh)/3 + 100vh);
}

/* 再分别设置每个.background的background-image */
/* .content....此处省略 */

视差的原理的话,可以看看张鑫旭大大的这篇,讲的很详细

实不相瞒,我是看了他这篇才做出来的(滑稽)

简单来说,就是通过3D变换使背景元素(.background)远离视点,同时放大 覆盖整个视角。滚动时由于背景元素离视点更远,看起来就像是背景滚动得更慢。

Demo中有5个.row,其中#row0#row2#row4都分别有背景元素,有视差效果。而#row1#row3没有背景元素。

这里主要说下:

背景元素的变换

/* translate3d的三个参数分别相当于translateX, Y和Z */
transform: translate3d(-2vw, -100vh, -2px) scale(3.05);
transform-origin: top;
height: calc((100% - 100vh)/3 + 100vh); 

translate

本来应该是translate3d(0, -100vh, -2px) scale(3);

前面设置了.parallax-stage的视距为1px,这里再偏移-2px就是3倍距离,然后设置scale(3)使得大小看起来和变换之前一样。实现背景元素的滚动速度为1/3的效果。

-100vh是为了让背景元素对齐视角顶部,设置transform-origin: top;以顶部为中心缩放。

不知道是滚动条还是什么原因,正常的translate3d(0, -100vh, -2px) scale(3)会留一点白边在左侧,并不能完全覆盖视角。

不过简单地设置为translate3d(-2vw, -100vh, -2px) scale(3.05)就能解决,我也没深究其原因。

height

背景元素的高度为calc((100% - 100vh)/3 + 100vh),是一个计算值。

当背景以1/3的速度滚动时,这个值刚好合适,能够使背景元素在滚动时刚好贴合视角。怎么算的我就不说了,这个涉及数学了不好表述,简单算下应该就能得到。

如果要让背景以1/2或者其他速度滚动,这个就要重新计算。

为什么要有.bg-container

为什么不直接把背景元素放在.row 里,而是放在.bg-container里,再放到.row里?

这里是因为,如果一个元素同时设置overflow: hidden;transform-style: preserve-3d;transform-style: preserve-3d;很多时候会失效(但新版chrome能正常显示),原因未知。

这里你肯定不希望变换之后的背景元素超出.row,所以.row要设置overflow: hidden;,却不能同时设置transform-style: preserve-3d;,只能将其设置在.bg-container上。

结束语

由于疫情,这都4月了还没开学。昨天听说了高考延期一个月,估计中考也会延期(没错我初三)。

这样下去我中考体育完了。我也不想延期,毕竟学得比我认真的太多了,延期我也超越不了他们。

中国教育就这样,只有往死里学,才能得到不至于太糟的教育条件。只重视学科成绩,兴趣什么的毫无用处。

想跳出中国教育的高墙?那得有钱。

此博客所有文章默认采用署名—非商业性使用—相同方式共享 4.0 协议进行许可
本文链接:https://blog.texice.xyz/2020/CSS-Parallax/