1
背景: 前几天在做项目的过程中需要更改div的scrollTop,要求运动过程类似IOS Webview可视区吸顶动画,先快后慢

分析:

  1. css不能设置offsetTop,需要JS动态更改offsetTop。
  2. 动画先快后慢意味着,scrollTop更改的速度越来越慢
  3. 移动总体的距离是scrollTop。
  4. 事件控制在两秒之类,小于一屏幕1s,大于一屏幕2s
  5. 速度??距离?? 速度在时间上的积分等于距离???? 定积分!!!!

猜想函数:

什么函数是递减的??

什么函数做定积分好做。换句话说,原函数好找??

什么函数作用域包涵0(因为需要从0时刻开始)

什么函数在0-2S的积分为scrollTop

盲猜 y = 1 / x;

绘图地址

clipboard.png

作用域不包含0 ??? 向右平移一个单位 ??? 左加右减,上加下减!!!哈哈哈哈 高数老师的敦敦教导= =

y = 1 / (x + 1);

clipboard.png

y = 1 / (x + 1); 在0-2s的积分一定是scrollTop吗 ????

需要一个系数????

y = K / (x + 1) ;

怎么求出这个系数呢??

y = K / (x + 1);

y 在 0 - 2上的积分 = scrollTop;

y = 1 / x的原函数 f(x) = Math.log(x) + c, c为常数;

类推求出 y = K / (x + 1)的原函数 f(x) = Math.log(x + 1) + c, c为常数;

最终实现代码如下:

clipboard.png

优化性能
setInterval实现这种效果只是使用20ms作为offset更改的时间点
更高的优化 ???? requestAnimFrame!!!MDN requestAnimationFrame

核心思路: 获取两次递归的事件间隔??? new Date().getTime() - startTime

最终实现如下:

clipboard.png


完整代码 
    <!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>测试scroll</title>

    <style>

        * {
            padding: 0;
            margin: 0;
        }

        .scroll {
            height: 400px;
            overflow: scroll;
            width: 100px;
            background-color: red;
        }

        .scroll div:nth-child(even) {
            height: 60px;
            background-color: red;
        }

        .scroll div:nth-child(odd) {
            height: 60px;
            background-color: green;
        }

        .button {
            position: fixed;
            right: 0;
            top: 0;
            height: 30px;
            width: 100px;
            background-color: red;
            font-size: 12px;
            line-height: 30px;
            text-align: center;
        }
    </style>

    <script>
        window.addEventListener('load', () => {
            const scrollDOM = document.querySelector('.scroll');
            const btnDOM = document.querySelector('.button');

            window.requestAnimFrame = window.requestAnimationFrame
                || window.webkitRequestAnimationFrame
                || window.mozRequestAnimationFrame;

            btnDOM.addEventListener('click', () => {
                console.log(scrollDOM.scrollTop);
                let scrollTop = scrollDOM.scrollTop;

                window.requestAnimFrame
                    ? window.requestAnimFrame(() => requestFrameUpdateScrollTop(new Date().getTime(), scrollTop))
                    : setTimeUpdateScrollTop(scrollTop);
            });

            const requestFrameUpdateScrollTop = (startTime, scrollTop) => {
                const timeStep = 20;
                // 总时间 2000 ms
                const allX = 2000 / timeStep;
                let startX = 0;

                // 系数
                const ratio = scrollTop / Math.log(allX + 1);
                // 定积分可得
                const speed = ratio * (1 / (startX + 1));
                const time = new Date().getTime();
                const timeDiff = time - startTime;
                startX += Math.floor(timeDiff / timeStep) + 1;
                scrollTop = scrollTop - speed * startX;
                scrollTop = scrollTop <= 1 ? 0 : scrollTop;
                scrollDOM.scrollTop = scrollTop;
                if (scrollTop > 0) {
                    window.requestAnimFrame(() => requestFrameUpdateScrollTop(Date.now(), scrollTop));
                }
            };

            const setTimeUpdateScrollTop = scrollTop => {
                if (this.timer) {
                    return;
                }

                const timeStep = 20;
                // 总时间 2000 ms
                const allX = 2000 / timeStep;
                let startX = 0;
                // 系数
                const ratio = scrollTop / Math.log(allX + 1);
                // 定积分可得

                this.timer = setInterval(() => {
                    const speed = ratio * (1 / (startX + 1));
                    console.log(speed);
                    startX++;
                    scrollTop = scrollTop - speed;
                    scrollDOM.scrollTop = scrollTop < 0 ? 0 : scrollTop;
                    if (scrollTop <= 0) {
                        clearInterval(this.timer);
                        this.timer = null;
                    }
                }, timeStep);
            };
        });
    </script>
</head>
<body>
<div class="scroll">
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
    <div>asdasd</div>
</div>

<div class="button">
    开始123123123运动
</div>
</body>
</html>

这是先快后慢的动画, 其他动画的解决思路同理
核心:

 找函数 ,做积分!!!
 找函数 ,做积分!!!
 找函数 ,做积分!!!

最后说句:如果能用css实现,少用JS


十月安徒生
108 声望2 粉丝