zoukankan      html  css  js  c++  java
  • JavaScript中该如何[更好的]做动效

    在用js写动画的时候,无非使用 setTimeout/setInterval 或者 requestAnimationFrame 来处理动画(在jquery的代码里也是这么干的),本文主要为了记录下两者的区别及使用两者来实现动过程。

    以实现一个简单的滚动到顶部为例

    setInterval

    setInterval() 方法重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间延迟。返回一个intervalID,可用于 cancelInterval 达到结束循环的效果。

    setTimeout 和 setInterval 的实现基本没区别,一个是定时执行,一个是定时循环执行,前者加个自己调用自己就是后者了,下面主要以 setInterval 为代表

    实现过程:

    1.写个方法,该方法需要传入一个代表动画所需执行的时间的参数(如:滚动到顶部需要1000毫秒)

    function doAnimate(duration){
        return function(){
            // do something
        }
    }

    2.取当前页面距顶部高度、滚动速度(以匀速为例)、写个开始动画的函数(为了给addEventListener绑事件传参,其实也可直接 dom.onclick = fn )

    function doAnimate(duration){
        return function(){
            var start = document.documentElement.scrollTop;
            var scrollSpeed = start/duration*(1000/60); // 以大多浏览器的刷新频率60帧(60Hz)为准  1秒60次的刷新
            var timer;
            var startTime = +new Date(); // 标记时间,仅供后面测效果用而已
            function startAnimate(){
                timer = setInterval(function () {
                    // do something
                },1000/60)
            }
        }
    }

    3.写滚动动画

    function doAnimate(duration){
        return function(){
            var start = document.documentElement.scrollTop;
            var scrollSpeed = start/duration*(1000/60); // 这里取到平均滚动距离,以大多浏览器的刷新频率60帧(60Hz)为准,1秒60次的刷新
            var timer;
            function startAnimate(){
                timer = setInterval(function () {
                    document.documentElement.scrollTop = start < scrollSpeed ? start -= start : start -= scrollSpeed; // 当前该滚动到何处,如果距离顶部小于平均滚动距离,直接滚到scrollTop为0;如果大于,则取到的触发时高度以平均滚动距离递减
                    if(start === 0){
                        clearInterval(timer);
                    }
                },1000/60)
            }
        }
    }

    4.写个很高的页面、给个div、加个click事件触发滚动回顶部

    html

    <!-- 为了更好的看到滚动效果及测滚动是否平滑,我们用某度的大图扔页面上 -->
    <img src="./来自百度壁纸的大图" />
    <img src="./来自百度壁纸的大图" />
    <img src="./来自百度壁纸的大图" />
    
    <div id="scrollTop_1000" style=" 60px;height: 20px;background: #fff;position: fixed; bottom:50px;right:50px;text-align: center">1000</div>
    <div id="scrollTop_3000" style=" 60px;height: 20px;background: #fff;position: fixed; bottom:80px;right:50px;text-align: center">3000</div>

    js

    function doAnimate(duration){
        return function(){
            var start = document.documentElement.scrollTop;
            var scrollSpeed = start/duration*(1000/60); // 这里取到平均滚动距离,以大多浏览器的刷新频率60帧(60Hz)为准,1秒60次的刷新
            var timer;
            function startAnimate(){
                timer = setInterval(function () {
                    document.documentElement.scrollTop = start < scrollSpeed ? start -= start : start -= scrollSpeed; // 当前该滚动到何处,如果距离顶部小于平均滚动距离,直接滚到scrollTop为0;如果大于,则取到的触发时高度以平均滚动距离递减
                    if(start === 0){
                        clearInterval(timer);
                    }
                },1000/60)
            }
        }
    }
    document.getElementById('scrollTop_1000').addEventListener('click',doAnimate(1000),!1)
    document.getElementById('scrollTop_3000').addEventListener('click',doAnimate(3000),!1)

    效果如图:

    截图分别测了设置 duration 为1000和3000的滚动效果 

    requestAnimationFrame

    requestAnimationFrame() 方法告诉浏览器您希望执行动画,并请求浏览器调用指定的函数在下一次重绘之前更新动画。该方法将在重绘之前调用的回调作为参数。返回一个 requestID ,可用于 cancelAnimationFrame 达到取消 requestAnimationFrame 动画的效果。

    实现思路如上,代码如下:

    html

    <!-- 为了更好的看到滚动效果及测滚动是否平滑,我们用某度的大图扔页面上 -->
    <img src="./来自百度壁纸的大图" />
    <img src="./来自百度壁纸的大图" />
    <img src="./来自百度壁纸的大图" />
    
    <div id="scrollTop_1000" style=" 60px;height: 20px;background: #fff;position: fixed; bottom:50px;right:50px;text-align: center">1000</div>
    <div id="scrollTop_3000" style=" 60px;height: 20px;background: #fff;position: fixed; bottom:80px;right:50px;text-align: center">3000</div>

    js

    function doAnimate(duration){
        return function(){
            var start = document.documentElement.scrollTop;
            // 获取实时时间
            var nowTime = function(){
                return +new Date
            }
            // 开始时间 用于计算动画运行时间和动画规定时间的百分比
            var startTime = nowTime();
            var animateId;
            var startAnimate = function() {
                animateId = requestAnimationFrame(toTop);
            }
            var stopAnimate = function() {
                cancelAnimationFrame(animateId)
            }
            function toTop(){
                // 剩下时间
                var restTime = Math.max(0, duration - ( nowTime() - startTime))
                var percent = restTime / duration || 0;
                var changeStyle = function(value){
                    document.documentElement.scrollTop = value;
                }
                // 根本比例获取剩下的距离,也就是实时距离顶部的距离
                var distance = start * percent;
                if(!distance){
                    changeStyle(distance)
                    stopAnimate();
                }else{
                    changeStyle(distance)
                    startAnimate();
                }
            }
            startAnimate();
        }
    }
    
    document.getElementById('scrollTop_1000').addEventListener('click',doAnimate(1000),!1)
    document.getElementById('scrollTop_3000').addEventListener('click',doAnimate(3000),!1)

    效果如图:

    没区别,没毛病,然而并没有和上面用同一张图...(其实打印下时间,会发现 setInterval 会是1000毫秒以内,大致在960-980毫秒之间,这个梗哪位大神可知???求解!!!)

    两者的区别

    requestAnimationFrame 会请求浏览器调用指定的函数在下一次重绘之前更新动画,所以开发者不用考虑频率/丢帧问题

    setInterval 中,会因为浏览器显示频率和 JavaScript 单线程可能会引发阻塞的问题而导致丢帧(视觉应为动画不流畅)

    requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,性能方面更出色

    对于隐藏或者不可见的元素,requestAnimationFrame 将不会进行重绘或回流,这点可减少cpu,gpu及内存的负荷

    setInterval 兼容一些老版本的浏览器(jquery保留这个应该也是为了兼容老版本浏览器...)

    requestAnimationFrame 兼容图


    顺便扔上jquery里animate的部分代码:

    jQuery.fx.start = function() {
        if ( !timerId ) {
            timerId = window.requestAnimationFrame ?
                window.requestAnimationFrame( raf ) :
                window.setInterval( jQuery.fx.tick, jQuery.fx.interval );
        }
    };
    
    jQuery.fx.stop = function() {
        if ( window.cancelAnimationFrame ) {
            window.cancelAnimationFrame( timerId );
        } else {
            window.clearInterval( timerId );
        }
    
        timerId = null;
    };

    略显尴尬... 在我windows上和mac上也保留一些老版本的浏览器,测效果的结果简直蛋疼...看来兼容方面还是需要做处理的,天将降大任于 setInterval 啊    :-D

    欢迎交流 欢迎指出各个问题~

  • 相关阅读:
    TOJ 2776 CD Making
    int型、long型和long long型
    Hadoop HA- hadoop集群部署
    大数据之路- Hadoop环境搭建(Linux)
    Hadoop HA- zookeeper安装配置
    Zookeeper- Error contacting service. It is probably not running解决方案和原理
    大数据- 自定义Log4j日记
    Hadoop- Hadoop环境搭建
    域名解析
    JAVA- JDBC之DBHelper
  • 原文地址:https://www.cnblogs.com/ys-ys/p/6919395.html
Copyright © 2011-2022 走看看