requestAnimationFrame

动画

web开发中实现动画的方式有多种,CSS3中的transition和animation,js中的setInterval、setTimeout、canvas。H5新API requestAnimationFrame。

屏幕的刷新频率

屏幕的刷新频率即图像在屏幕上每秒更新的次数,单位为HZ。该值受屏幕分辨率、屏幕尺寸和显卡的影响。每一次称之为帧,一般的屏幕为60HZ,则一帧的时间为16.7ms左右。

setInterval动画存在的问题

setInterval 其实就是通过设置一个间隔时间来不断的改变图像的位置,从而达到动画效果的。但有时候会发现,利用setInterval实现的动画在某些低端机上会出现卡顿、抖动的现象。 这种现象的产生有两个原因:

  1. setInterval 的执行时间并不是确定的。js执行时,setInterval 会被放进了异步队列中,只有当主线程上的任务执行完以后,才会去检查该队列里的任务是否需要开始执行,因此 setInterval 的实际执行时间一般要比其设定的时间晚一些。

  2. 屏幕的刷新频率受分辨率和尺寸的影响,因此不同设备的屏幕刷新频率可能会不同,而 setInterval 只能设置一个固定的时间间隔,这个时间不一定和屏幕的刷新时间相同,所以会引起丢帧从而出现卡顿现象。

requestAnimationFrame

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。

优势:

requestAnimationFrame 比起 setTimeout、setInterval的优势主要有两点:

  1. requestAnimationFrame 是由系统来决定回调函数的执行时机。它会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随屏幕的刷新频率,不会引起丢帧和卡顿。
  2. 使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。
用法:
window.requestAnimationFrame(callback)
回调参数:

回调函数会被传入DOMHighResTimeStamp参数,DOMHighResTimeStamp指示当前被 requestAnimationFrame() 排序的回调函数被触发的时间。

返回值:

返回整数,即请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。

案例:

  1. 普通用法:
<div class="box"></div>
<button class="btn">click</button>
const box = document.querySelector('.box')
const btn = document.querySelector('.btn')

btn.addEventListener('click', () => {
  let start

  function handler(timestamp) {
    if (!start) start = timestamp

    const progress = timestamp - start
    start = timestamp

    console.log(progress)

    if (box.offsetWidth < 100) {
      box.style.width = `${box.offsetWidth + 1}px`
      box.innerHTML = box.offsetWidth + '%'

      requestAnimationFrame(handler)
    }
  }

  requestAnimationFrame(handler)
})
  1. 取消回调
<div class="main">
  <div class="content"></div>
  <button class="button">click</button>
</div>
const content = document.querySelector('.content')
const button = document.querySelector('.button')

button.addEventListener('click', () => {
  let rfaId
  let left = 0

  function handler() {
    if (content.offsetWidth < 100) {
      content.style.left = `${left++}px`

      if (left >= 100) {
        return cancelAnimationFrame(rfaId)
      }

      rfaId = requestAnimationFrame(handler)
    }
  }

  rfaId = requestAnimationFrame(handler)
})
  1. 自定义raf时间

自定义raf时间通常是放慢刷新频率,毕竟系统的16.7ms已经是正常的,再快也没什么意义。

  <div class="main"></div>
  <button class="btn">click</button>
const element = document.querySelector('.main')
const btn = document.querySelector('.btn')

btn.addEventListener('click', () => {
  // 自定义的动画时间差
  const time = 50
  let left = 0
  let flag = true
  // 当前执行时间
  let nowTime = Date.now()
  // 每次动画执行结束的时间
  let lastTime = Date.now()

  // 执行动画
  function handler() {
    nowTime = Date.now()

    if (nowTime - lastTime >= time) {
      lastTime = nowTime

      if (flag) {
        if (left <= 100) {
          element.style.left = `${left++}px`
        } else {
          flag = false
        }
      } else {
        if (left >= 0) {
          element.style.left = `${left--}px`
        } else {
          flag = true
        }
      }
    }

    requestAnimationFrame(handler)
  }
  requestAnimationFrame(handler)
})

当然,该API的用途不止设置动画,还可以用于节流、防抖等。比如lodash关于节流、防抖的就是基于此API实现的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,547评论 4 374
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,787评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,175评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,752评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,169评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,056评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,195评论 2 321
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,980评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,724评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,872评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,344评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,667评论 3 264
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,379评论 3 245
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,202评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,992评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,189评论 2 286
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,987评论 2 279

推荐阅读更多精彩内容