CSS 实现Staggered3DGridAnimations

490 阅读5分钟

起因

网上冲浪看到一个非常好看的效果,Staggered3DGridAnimations

01.jfif

其中的效果是采用了GSAP动画库来实现,本次是挑战使用CSS来完成!

分析效果

01.gif

整个效果是基于滚动的进度来动态改变图片的模糊偏移缩放,以及文本的偏移

重点是根据滚动的进度!!那我们第一个应该考虑的就是CSS滚动驱动动画了,如果对这个属性很陌生没了解过的同学可以看这篇博客~CSS 滚动驱动动画-animation-timeline: scroll尝鲜,省流版就是之前的动画都是线性的,从开始 -> 结束无法再干涉,而滚动驱动动画则是事件驱动,会根据滚动的距离来动态改变动画进度!!

布局

<div class="grid">
    <figure class="grid__item">
        <div class="grid__item-imgwrap">
            <div class="grid__item-img" style="background-image:url(./img/1.jpg)"></div>
        </div>
    </figure>
    <figure class="grid__item">
        <div class="grid__item-imgwrap">
            <div class="grid__item-img" style="background-image:url(img/2.jpg)"></div>
        </div>
    </figure>
    <figure class="grid__item">
        <div class="grid__item-imgwrap">
            <div class="grid__item-img" style="background-image:url(img/3.jpg)"></div>
        </div>
    </figure>
    <figure class="grid__item">
        <div class="grid__item-imgwrap">
            <div class="grid__item-img" style="background-image:url(img/4.jpg)"></div>
        </div>
    </figure>
    ...
    ...
</div>    

图片布局比较简单就是一个grid布局,图片的角度偏移已经模糊效果都非常简单,代码如下

 transform: translate(40%, 100%) translate3d(0px, 0px, 300px) rotate(-5deg) rotateX(70deg) skew(20deg, 0deg);
 
 filter: blur(7px) brightness(0%) contrast(400%);

因为要区分左右两列每列的旋转方向不同所以我们还要用:nth-child(even):nth-child(odd)来区分!

  • odd 表示元素在兄弟元素列表中的位置是奇数:1、3、5……。

  • even 表示元素在兄弟元素列表中的位置是偶数:2、4、6……。

02.png

滚动驱动动画!

基础的布局效果已经完成,就该开始着手动画了,观察效果图其实能发现整个动画的效果就是由模糊、缩放、偏移 -> 不模糊、不缩放、不偏移 -> 再模糊、再缩放、再偏移的过程,代码如下

@keyframes appear-even {
    0% {
        transform: translate(40%, 100%) translate3d(0px, 0px, 300px) rotate(-5deg) rotateX(70deg) skew(20deg, 0deg);
        filter: blur(7px) brightness(0%) contrast(400%);
    }

    30% {
        filter: blur(0px) brightness(100%) contrast(100%);
    }

    50% {
        filter: blur(0px) brightness(100%) contrast(100%);
        transform: translate(0%, 100%) translate3d(0px, 0px, 0px) rotate(0deg) rotateX(0deg) skew(0deg, 0deg);
    }

    100% {
        filter: blur(4px) brightness(0%) contrast(500%);
        transform: translate(20%, 0%) translate3d(0px, 0px, 300px) rotate(1deg) rotateX(-50deg) skew(-10deg, 0deg);
    }
}

如果我们直接使用上述的动画效果如下

02.gif

这就是我们最开始说的动画是线性的从开始到结束不受控制,所以我们要使用滚动事件来驱动我们的动画进度!!

关于滚动驱动动画有两种使用方式

  • 用户滚动时发生的动画,动画的进度与滚动进程明确相关。例如,长篇文章的进度条。 animation-timeline: scroll
  • 当元素进入、退出或穿过可见区域时发生的动画。view-timeline

关于我们的图片效果肯定是要基于元素自身进入 -> 退出可见区域来动态改变!

view-timeline

view-timeline接受两个值第一个是view-timeline-name第二个是view-timeline-axis

  • view-timeline-name是为了我们使用animation-timeline来触发滚动驱动效果是自定义一个名字
  • view-timeline-axis则是为了确认绑定哪个滚动方向来触发动画进度

我们的例子中很明显应该和垂直方向的滚动绑定

.grid__item:nth-child(odd) .grid__item-imgwrap {
    animation: appear auto linear both;
    view-timeline: --subjectReveal block;
    animation-timeline: --subjectReveal;
}

.grid__item:nth-child(even) .grid__item-imgwrap {
    animation: appear-even auto linear both;
    view-timeline: --subjectReveal block;
    animation-timeline: --subjectReveal;
}

03.gif

可以发现基础的效果已经实现了就是感觉图片消失的有一些提前了这是因为view-timeline本身的进度是根据元素进入滚动窗口和离开窗口决定的!

04.png

如果我们需要改变这个默认位置计算需要用到view-timeline-inset属性

view-timeline-inset

CSS属性用于指定一个或两个值,表示对滚动端口位置的调整,这允许您指定偏移时间轴位置的起始和/或结束插入(或起始)值。

值得注意的是view-timeline-inset值的正负对偏移量是有影响的!

  • 如果该值为正,动画的开始/结束位置将在滚动口内移动指定的长度或百分比。
  • 如果值为负数,动画的开始/结束位置将按指定的长度或百分比移出滚动口,即在出现在滚动口之前开始动画,或者在离开滚动口后完成动画。

05.png

所以我们如果想要调整图片消失的时机只需要把结束的值设置为负数即可!

04.gif

animation-timeline: scroll

此时的效果已经和原效果非常贴近了,接下来就是把中间文字平移加上!关于中间这个文字平移我们就不能用视图驱动了,观察可以发现文字的平移完全基于容器的滚动距离,那就更简单了!


<div class="mark">
    <div class="mark__inner font-alt">
        <span>Sora Takahashi</span> <span>空 高橋</span>
        <span>Aoi Nakamura</span> <span>葵 中村</span>
        <span>Ren Fujimoto</span> <span>蓮 藤本</span>
        <span>Mio Sakurai</span> <span>澪 桜井</span>
        <span>Shin Yamamoto</span> <span>真 山本</span>
        <span>Kaori Kobayashi</span> <span>香織 小林</span>
        <span>Hikari Inoue</span> <span>光 井上</span>
        <span>Yuki Kinoshita</span> <span>雪 木下</span>
        <span>Rina Ishikawa</span> <span>莉奈 石川</span>
        <span>Kaito Matsumoto</span> <span>海斗 松本</span>
    </div>
</div>


.mark__inner {
    ...
    animation: scroll-hor auto linear;
    animation-timeline: scroll(root);
}

@keyframes scroll-hor {

    0% {
        transform: translateX(100vw);
    }

    100% {
        transform: translateX(-100%);
    }

}

效果展示

结语

年少心高逞英雄,披霞踏月追疾风!