requestAnimationFrame()

  本节课来讲解与与时间相关的三个方法setTimeout()、setInterval()和requestAnimationFrame(),这三个方法都属于浏览器的window对象的方法,在过去只持支定时器setTimeout()和它setInterval()方法, requestAnimationFrame()新增的一个方法,可以更好的服务于2Dcanvas动画、WebGL动画等方面。

倒计时setTimeout()

<script>
        function fun() {
            alert("定时器")
        }
        //2000ms,也就是2s后执行fun1函数
        setTimeout("fun()",2000);
</script>

定时周期执行setInterval()

<script>
    function fun() {
        document.write("定时器"+"<br>")
    }
    //间隔200ms周期性调用函数fun
    setInterval("fun()",200);
</script>

时间延迟现象

  在fun函数里面嵌入一段for循环程序,把i的判断条件设置一个较大的值1000000000,刷新浏览器,你通过视觉观察网页插入文字document.write("定时器"+"
")的速度变慢,200ms将得不到准到执行, 这很正常,程序的执行都是需要时间的,如果for循环占用的时间比较小的话,就不会阻塞setInterval()对函数fun()的调用,使用setInterval()和setTimeout()方法的时候要注意时间的问题。

<script>
    function fun() {
        for(let i = 0;i<1000000000;i++){
            //借助for循环多次执行延时
        }
        document.write("定时器"+"<br>")
    }
    //间隔200ms周期性调用函数fun
    setInterval("fun()",200);
</script>

取消定时器

方法 作用
clearInterval() 取消setInterval()设置的定时器
clearTimeout() 取消setTimeout()设置的定时器

  在实际的应用定时器的时候,会通过Javascript代码来添加或取消定时器,比如你只是希望一段时间内周期性执行某段代码,只使用setInterval()方法是不够的,超出时间区域后要使用clearInterval()方法清除定时器。

requestAnimationFrame()

  执行过程:代码初始化后,fun();语句调用fun函数,然后开始执行函数{}里面的的程序,document.write("定时器"+"<br>");会在页面插入代码“定时器"+"<br>”,显示“定时器”三个字并换行。 执行到requestAnimationFrame(fun);语句,这个方法的作用是向浏览器发送请求我希望你稍后调用函数fun(),这是fun函数后面还有程序S++;还没执行要执行后才可能执行fun函数,执行完S++;后,没有什么程序占用浏览器资源, 这时会相应浏览器的请求开始再次执行fun()函数,但是这里要注意一个问题,如果其它程序都很少,假设占用执行时间1ms,浏览器不会每间隔1ms相应requestAnimationFrame(fun);的请求,会适当延迟,默认是每秒调用60次, 大约16.7ms的执行周期,如果显示的是动画也就是60FPS,实际的工程中可能达不到60FPS,主要是因为实际的程序不可能像下面的主循环函数fun只有三句代码,往往要占用一定时间,比如占用25ms,那就意味着动画刷新效果是 40FPS。

<script>
    let S = 0;
    function fun() {
        document.write("定时器"+"<br>");
//        发起下一次fun执行请求
        requestAnimationFrame(fun);
        S++;
    }
//    开启函数fun第一次执行
    fun();
</script>

  可能你会好奇requestAnimationFrame()为什么设定最大的执行频率是60,而不是更高,其实很简单,比如动画60FPS的刷新频率完全满足人类视觉,没必要更高,这样反而浪费有限的浏览器和计算机资源。

requestAnimationFrame()调用延迟

  可以刷新浏览器,查看下面代码的执行速度,和上面的程序进行对比。

<script>
    function fun() {
        for(let i = 0;i<1000000000;i++){
            //借助for循环多次执行延时
        }
        document.write("定时器"+"<br>");
        requestAnimationFrame(fun);
    }
    fun();
</script>

requestAnimationFrame()时间不稳定解决办法

  通过上面的代码学习可以知道requestAnimationFrame()发起请求后多长时间后浏览器响应是不确定的,在具体的应用的时候也不一定要定周期执行函数,主要刷新频率在30FPS以上就行,也就是说一般requestAnimationFrame() 调用函数执行的时间在16.7ms~33.3ms之间,可能是任何一个值,如果你制作canvas动画,希望执行函数得到一个均匀的速度,那就需要知道两次调用函数fun的时间间隔

<script>
    let x = 1;//位移变量
    let T0 = new Date();//上次时间
    function fun() {
    let T1 = new Date();//本次时间
    let t = T1-T0;//时间差
    x = t*100;//时间乘以速度100单位/ms等于距离
    requestAnimationFrame(fun);
    T0 = T1;//把本次时间赋值给上次时间
    }
    fun();
</script>

  通过Javascript语言的内置对象Date获得不同位置时间进行标记,然后就可以知道两次调用函数fun的时间差,可以把这个时间差作为变量代入公式计算。这时候也许你会想执行let t = T1-T0;这个语句的时候本身也是占用时间的, 那么t还精确吗?其实世界上没有绝对的精确,都是相对的,一般来说个人计算机CPU执行一条指令的时间大约几个ns,ns相比毫秒差了10的6次方个数量级,完全不会影响游戏等Web应用的性能。比如编写单片机电子钟程序的时候如果没有特殊要求精度, 可以用使用C语言,如果希望更高精度,那就使用汇编语言,计算出每一条指令的时间,降低误差。

   如果是普通前端,其实这些底层硬件问题不用深入研究,之所以写出来,是因为部分读者是有底层硬件基础的,也希望了解这部分知识,事实上随着时间的推移, 浏览器也可以成为工控上位机的载体,Javascript语言也可以加入嵌入式开发的行列。本人最初学习HTML5和Javascript也不是因为要从机械跳到互联网,而是机械、自动化、CAD等领域借助浏览器和互联网可以更好的提高工作效率。 比如3D打印的的切片软件可以运行在PC客户端上,HTML5是支持WebGL的,自然切片软件也可以运行在Web页面上,3D打印机也可以入网远程接受3D打印的云点数据,甚至远离互联网机械领域的机加工设备也可以通过网页远程控制,这只是时间问题。 或许有一天,前端工程师被拉过去写机床的Web控制界面,当年从机械、电子转到前端的工程师或许也会偶然成为复合程序员,跨行的劣势成为优势。

cancelAnimationFrame()

  cancelAnimationFrame()方法的作用就是取消requestAnimationFrame()对某个函数的对用,比如函数fun(),直接把函数名字变量作为cancelAnimationFrame()的参数即可,格式cancelAnimationFrame(fun)。