使用粘性定位做一个动画效果

199 阅读3分钟

前言

最近在公司写代码的时候,浏览到百度智能云的千帆AI应用开发者中心的首页动态效果,利用特性巧妙完成,给人一种高级感。,当时也并没有看出来他们是怎么写的,只觉得不错。我本来是不打算写这玩意,所有也没太放在心上。谁敢想这组长跟产品说这个效果要加到新的首页页面。鸟语花香。。。

展示看看

image.png 我还想放个视频在这给大伙看看,不曾想这里只支持西瓜视频,吐槽一下,连抖音视频都还不支持。 那就大伙自己去这个网站看看效果了。如图就是我截图的这部分区域。

它的效果就是会在滚动到顶部就吸住,然后下方的B容器会跟上来把他盖住,并且他自己也会缩小。

这里有个问题:文字固定后,下方的A容器也固定住了,说明文字和A容器是同一个容器,B容器在盖住A容器后并没有盖住文字?

解析问题

这里很容易看出来A容器明显使用了粘性定位。这下方是整个A、B容器的结构。查看样式也就是一个粘性定位加上一个缩放属性。

这里教一些新手一个技巧,当有一些效果不知道怎么写,可以上网站检测他们的样式css和DOM结构,类似于下方图上所示。

image.png

当时我还没想清楚没有其他style那是怎么把上方的问题解决的?难道用了JS?那不得蛮复杂的。 其实问题就在粘性定位上,上方的问题能不能想到完全就取决于你够不够了解它。

聊聊粘性定位

我们在学习粘性定位时都知道在没有到达阈值时都只是类似于relative,没有效果;到达阈值后将变成固定定位。

诶,就是这个固定定位迷惑到你了,固定定位会相对于视口进行定位,脱离文档流。粘性定位生效后不会脱离文档流,拥有高度,能挤开其他容器。只是一个半固定。像非牛顿流体,又硬又软的。

说到这里是不是就没什么疑惑了。所以B容器没有盖住文字是因为不够高,当完全盖住A容器的非文字部分后就因为父容器的高度不够后随着一起滚动离开视口了,A容器的粘性定位也是因为父容器的高度不足以支撑随即失效。因此唯一一处有js的地方就是A容器内部的缩放了。

容器缩放

如果使用框架的话,会比较好操作,唯一有点难度的是如何控制缩放在上下滚动时放大和缩小。

首先先模拟他的DOM结构。放上一个head用于增加滚动距离。all容器就是我们的主要展示区域的父容器了。在其中放上文字,让contant容器和box2容器一样高。

<div class="head"></div>
    <div class="app">
        <div class="all">
            <div id="box" class="box1">
                <h1>hello</h1>
                <div id="contant" class="contant"></div>
            </div>
            <div class="box2">

            </div>
        </div>
        <div class="box3"></div>
        <div class="box4"></div>
    </div>

js附赠在这里,这里其实也不难,就是看上去长一点,看不懂的变量可以自己切换。核心就是利用getBoundingClientRect判断容器是否达到容器的粘性阈值,再利用window的滚动距离判断滚动方向来实现。

<script>
        const box = document.getElementById('box');
        const content = document.getElementById('contant');
        // 初始大小和最小缩放比例
        const INITIAL_SCALE = 1;
        const MIN_SCALE = 0.5;
        let scale = INITIAL_SCALE;
        // 关键距离点
        const TRIGGER_DISTANCE = 100;
        // 记录上次滚动位置,用于判断滚动方向
        let lastScrollY = window.scrollY;

        window.addEventListener('scroll', function () {
            const boxRect = box.getBoundingClientRect();
            const distance = boxRect.top;
            const currentScrollY = window.scrollY;
            const scrollDirection = currentScrollY > lastScrollY ? 'down' : 'up';
            lastScrollY = currentScrollY;
            // 当元素顶部距离小于等于TRIGGER_DISTANCE时开始处理缩放
            if (distance <= TRIGGER_DISTANCE) {
                if (scrollDirection === 'down') {
                    // 往下滚动,减小缩放比例
                    scale = Math.max(MIN_SCALE, scale - 0.001);
                } else if (scrollDirection === 'up') {
                    // 往上滚动,增大缩放比例
                    scale = Math.min(INITIAL_SCALE, scale + 0.001);
                }
                // 应用缩放
                content.style.transform = `scale(${scale})`;
            } else {
                // 重置缩放
                scale = INITIAL_SCALE;
                content.style.transform = `scale(${scale})`;
            }
        });
    </script>