风和日丽,饶有兴致,翻开以前写的一个简单的动画插件,发现是用定时器写的,可是做为有追求的前端,一个问题怎么能有一种解决方案呢?故而,遍寻资料,终于看见曙光,让我查到了requestAnimationFrame 这个宿主对象的方法,也能能优雅的实现js动画!css
在咱们前端的传统中,在古老的ie称霸的年代,咱们想要实现动画,必需要借助setTimeout或setInterval这两个函数,下面咱们来思考一个问题:html
咱们让一个数字从0开始逐渐变大,到达100时在逐渐变小,如此往复 前端
那么,传统的定时器的写法应该怎么写呢?废话少说上代码html5
//css部分 <style> #a { font-size: 30px; } </style> 复制代码
//html部分 <div id="a"></div> 复制代码
//js部分 var e = document.getElementById("a"); var flag = true; var left = 0; function render() { if (flag == true) { if (left >= 100) { flag = false; } e.innerHTML = left++; } else { if (left <= 0) { flag = true; } e.innerHTML = left--; } } setInterval(function () { render(); }, 1000/60); 复制代码
以上写法即可以实现循环往复的变大变小的操做!浏览器
这种方法,可行吗?固然可行,完美吗?也还算完美,当忽然发现新大陆之后,定时器便完全被终结了,就好比,你用了苹果的Retina屏幕之后,发现再也回不去了是一个道理,你说1080p的屏幕完美吗?挺完美的,然而当你拿到Retina之后,直呼真香!bash
在了解requestAnimationFrame以前,咱们先来了解几个概念,阐述一下为啥requestAnimationFrame真香markdown
之因此咱们能看到动画,一些动画效果,彻底时由咱们的显示器在短期内不断播放一张张图片,当播放速率过快时,便造成了动画效果,而咱们的显示器在播放图片时,通常有一个播放的频率标准,咱们叫作屏幕刷新率,即图像在屏幕上更新的速度,也即屏幕上的图像每秒钟出现的次数,它的单位是赫兹(Hz)。通常状况下,当刷新率达到60hz基本咱们的肉眼就感受不到他是静态的了,变成了一个连贯的动画!异步
那你可知这是为何呢?函数
为何你感受不到这个变化? 那是由于人的眼睛有视觉停留效应,即前一副画面留在大脑的印象还没消失,紧接着后一副画面就跟上来了,这中间只间隔了16.7ms(1000/60≈16.7), 因此会让你误觉得屏幕上的图像是静止不动的。而屏幕给你的这种感受是对的,试想一下,若是刷新频率变成1次/秒,屏幕上的图像就会出现严重的闪烁,这样就很容易引发眼睛疲劳、酸痛和头晕目眩等症状。(跟主题没啥关系,强行科普一波)oop
因为高刷新率的存在,加上人眼睛的视觉停留效应,理解动画的原理就变得很是简单了。 画本质就是要让人眼看到图像被刷新而引发变化的视觉效果,这个变化要以连贯的、平滑的方式进行过渡。,若是一来,在咱们的浏览器,中就能看到连贯的动画效果
上面的讲述你应该已经大概知道定时器能实现动画效果了,其实他就是经过不断改变这个元素的位置或者值,来达到快速播放静图片的效果,从而造成一个完整的动画
然而因为定时器的在js中的执行方式,致使它有一些小小的瑕疵,虽然能够忍受,可是有更好的东西出来,为啥不淘汰掉他呢?
咱们知道定时器的执行时间并非肯定的。这是因为js是个单线程的语言,他必须使用异步,来解决一些须要延时执行这个问题,那么为何说定时器的执行时间不是肯定的呢?那就得来细数一下轮询了
Event Loop的是计算机系统的一种运行机制。JavaScript语言就采用这种机制,来解决单线程运行带来的一些问题。
在JavaScript中,任务被分为两种,一种宏任务(MacroTask)也叫Task,一种叫微任务(MicroTask)。
常见的宏任务有:script所有代码、setTimeout、setInterval、setImmediate(浏览器暂时不支持,只有IE10支持,具体可见MDN)、I/O、UI Rendering。
常见的微任务有:Process.nextTick(Node独有)、Promise、Object.observe(废弃)、MutationObserver
下面来简单学习一下Event Loop的执行过程
最后,微任务执行完毕,清空执行栈,拿到队列中的下一次Event Loop的内容,在开始执行,走到这里,你会发现,在定时器执行的时候,前面还有会一堆同步代码也须要时间,若是前面有个循环个三五百次的话,会很是浪费时间,这就暴露出了定时器的一个缺点:丢帧现象,就是每次间隔实际上是不肯定的,致使跟浏览器的刷刷新率匹配不上,有可能出现的丢帧现象!(后通过大佬更正,定时器丢帧的缘由仅仅是没有被浏览器的策略干涉,并非会被同步任务阻塞)
//这段代码能够证明 requestAnimationFrame(() => console.log("Hello World")); while (true); 复制代码
看完流程之后,请仔细参悟上图,会有收获的!
requestAnimationFrame是html5 提供的一个专门用于请求动画的API,顾名思义就是请求动画帧,他被封装在宿主对象中, window.requestAnimationFrame() 告诉浏览器——你但愿执行一个动画,而且要求浏览器在下次重绘以前调用指定的回调函数更新动画。该方法须要传入一个回调函数做为参数,该回调函数会在浏览器下一次重绘以前执行
//html <div id="a"></div> //js <script> var e = document.getElementById("a"); var flag = true; var left = 0; function render() { if (flag == true) { if (left >= 100) { flag = false; } e.innerHTML = left++; } else { if (left <= 0) { flag = true; } e.innerHTML = left--; } } // setInterval(function () { // render(); // }, 1000); (function animloop() { render(); var a=window.requestAnimationFrame(animloop); })(); 复制代码
到这里就能够结束了,可是忽然有冒出来个疑问,他怎么中止呢?
执行函数放回一个id是回调列表中惟一的标识。是个非零值,没别的意义。你能够传这个值给 window.cancelAnimationFrame() 以取消回调函数。
window.cancelAnimationFrame(a)//注意这个要写在函数体内部,
复制代码
到这里,基本就结束了requestAnimationFrame的探索,因为我也是边学边写,不对之处,请大佬指正!