requestAnimation -- 動(dòng)畫?

動(dòng)畫???

動(dòng)畫就是間隔一段進(jìn)行進(jìn)行一個(gè)操作.

一般常用的動(dòng)畫處理模式有早期的定時(shí)器: setTimeout , setInterval
后續(xù) css3 帶來(lái)了一個(gè)新的動(dòng)畫過(guò)度屬性 transition.

對(duì)于 transition 能實(shí)現(xiàn)動(dòng)畫很好的過(guò)度效果,這沒(méi)有什么可說(shuō)的.

但是對(duì)于 setTimeout , setInterval 來(lái)言. 雖然定時(shí)了,但可能執(zhí)行的時(shí)機(jī)并不準(zhǔn)確.

為什么?

這就要提高 JavaScript 的 EventLoop 了.

在瀏覽器 JavaScript 的執(zhí)行環(huán)境是單線程的.

只有等待主隊(duì)列同步代碼執(zhí)行完畢了,才會(huì)去執(zhí)行 EventLoop 事件循環(huán)里的回調(diào)函數(shù)(無(wú)一例外).

假設(shè)現(xiàn)在有這么一樣場(chǎng)景.

使用 setInterval , setTimeout 來(lái)定時(shí) 200ms 執(zhí)行一段代碼.

此時(shí),同步代碼有 1000 萬(wàn)行,執(zhí)行完畢時(shí)間要 半分鐘.

那么此時(shí),setInterval 和 setTimeout 雖然你設(shè)定了 200ms 后就執(zhí)行,
但由于同步代碼數(shù)量龐大,執(zhí)行的優(yōu)先級(jí)又最高.
于是就導(dǎo)致了即使是同步代碼運(yùn)行效率很快,不到半分鐘,25秒就可以執(zhí)行完的話.
那么你的 EventLoop 至少也需要等待25s后才能執(zhí)行 setInterval 和 setTimeout.

這顯然和之前設(shè)置的 200ms 差距太遠(yuǎn)了.

這也就是為什么說(shuō),在某些情況下, setInterval 和 setTimeout 會(huì)出現(xiàn)執(zhí)行時(shí)機(jī)不準(zhǔn)的問(wèn)題.

requestAnimationFrame

顯示器是需要刷新的.
瀏覽器跑在顯示里. 也肯定是需要刷新的.

看一段 MDN 的解釋:

window.requestAnimationFrame() 告訴瀏覽器——你希望執(zhí)行一個(gè)動(dòng)畫奉呛,并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動(dòng)畫锐极。該方法需要傳入一個(gè)回調(diào)函數(shù)作為參數(shù),該回調(diào)函數(shù)會(huì)在瀏覽器下一次重繪之前執(zhí)行
注意:若你想在瀏覽器下次重繪之前繼續(xù)更新下一幀動(dòng)畫迄埃,那么回調(diào)函數(shù)自身必須再次調(diào)用window.requestAnimationFrame()

瀏覽器內(nèi)部元素如果有動(dòng)畫的存在,瀏覽器是需要刷新的.

每刷新一次就叫做一幀.

如果能有一個(gè)時(shí)機(jī),在每次刷新下一幀之前,就把動(dòng)畫元素的變化執(zhí)行完畢.而不是不管時(shí)機(jī)胡亂的執(zhí)行,那么動(dòng)畫效果肯定會(huì)更流暢.

于是 requestAnimationFrame 函數(shù)就應(yīng)運(yùn)而生了.

它就是用來(lái)更加流暢的處理動(dòng)畫的.(為什么是動(dòng)畫?如果瀏覽器僅僅展示一片靜態(tài)的文件,為了節(jié)省系統(tǒng)資源,它就可以不刷新了或者降低刷新頻率.)

div#box,
    div#request-animation-box {
      width: 100px;
      height: 40px;
      background-color: salmon;
    }


    <div id="box"></div>
  <div id="request-animation-box"></div>
 // 使用定時(shí)器的方式,由于存在eventLoop導(dǎo)致代碼優(yōu)先級(jí)的問(wèn)題.會(huì)出現(xiàn)定時(shí)并不準(zhǔn)確的情況.
  let box = document.querySelector('#box'), rbox = document.querySelector('#request-animation-box')
  normalWay()
  function normalWay() {
    let id = setInterval(() => {
      box.style.width = `${++box.clientWidth}px`
      if (box.clientWidth > 300) clearInterval(id)
      console.log(box.style.width)
    }, 17); // 1000ms / 17 = 每分鐘 60 fps
  }

  // rWay
  rWay()
  function rWay() {
    rbox.style.width = `${++rbox.clientWidth}px` // 場(chǎng)景內(nèi),有物體的變化,必須刷新瀏覽器
    if (rbox.clientWidth <= 300)
      requestAnimationFrame(rWay) // 因?yàn)楸仨氁⑿聻g覽器,所以會(huì)調(diào)用 requestAnimationFrame . 
    // requestAimationFrame 的參數(shù)是一個(gè)函數(shù).
    // 但是只調(diào)用一次,它只會(huì)在下一幀渲染之前調(diào)用.后續(xù)幀刷新時(shí),就不會(huì)調(diào)用.
    // 所以需要不停的調(diào)用這個(gè)函數(shù)才可以.
  }

一句話解釋就是:

瀏覽器說(shuō): 我馬上要刷新下一幀了,你有啥事情(動(dòng)畫)需要我在刷新下一幀之前做完的嗎? 有的話趕緊給我.

我說(shuō): 有啊!我需要你在每下一次幀數(shù)渲染之前執(zhí)行 rWay 方法.

瀏覽器說(shuō): 好的.我會(huì)讓我的小弟去干這個(gè)事情.對(duì)了,忘了告訴你了,我小弟的名字叫 requestAnimationFrame


沒(méi)有動(dòng)畫呢?

上面都說(shuō) requestAnimaitonFrame , 動(dòng)畫,動(dòng)畫,和動(dòng)畫相關(guān).
那我的頁(yè)面里沒(méi)有動(dòng)畫邏輯,只有數(shù)據(jù)邏輯呢?

僅僅 1000 個(gè)數(shù)字的循環(huán),基本一瞬間就可以完成.

let counter = 1
  function nothingInScreenChanged() {
    ++counter
    console.log(counter)
    if (counter < 1000) { 
      nothingInScreenChanged()
    }
  }

  nothingInScreenChanged()

但是如果數(shù)字的循環(huán)和 reqeustAnimationFrame 添加上聯(lián)系呢? 這里并沒(méi)有我們可以在肉眼看到的變化,僅僅是程序內(nèi)部的數(shù)值發(fā)生了改變.

 function nothingInScreenChanged() {
    ++counter
    console.log(counter)
    if (counter < 1000) { // 受制于屏幕刷新率最大 60fps , 數(shù)據(jù)自增的速度會(huì)明顯變慢
      requestAnimationFrame(nothingInScreenChanged)
    }
  }

  nothingInScreenChanged()


總結(jié):

  1. 瀏覽器的刷新機(jī)制是客觀存在的.
  2. requestAnimationFrame 僅僅只是在瀏覽器每一次刷新渲染新幀之前的一個(gè)切片或者說(shuō)一個(gè)時(shí)機(jī).你用或者不用,這個(gè)切片都在那里.
  3. 它和你是否執(zhí)行動(dòng)畫效果,還是單純的業(yè)務(wù)邏輯代碼并沒(méi)強(qiáng)求.我們可以將任意代碼都丟在這個(gè)切片里面執(zhí)行.
  4. 有時(shí)候,一些業(yè)務(wù)代碼要求的是執(zhí)行效率,丟在在這個(gè)切片里,受制于瀏覽器刷新性能,反而會(huì)拖慢整個(gè)流程的運(yùn)行.

reqeustAnimation 不要一提到,就是動(dòng)畫動(dòng)畫. 它僅僅是一個(gè)瀏覽器在每一幀渲染之間給予我們的一個(gè)切片時(shí)機(jī).僅此而已.

碼云地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌节槐,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異铜异,居然都是意外死亡地来,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門熙掺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)未斑,“玉大人,你說(shuō)我怎么就攤上這事币绩±啵” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵缆镣,是天一觀的道長(zhǎng)芽突。 經(jīng)常有香客問(wèn)我,道長(zhǎng)董瞻,這世上最難降的妖魔是什么寞蚌? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮钠糊,結(jié)果婚禮上挟秤,老公的妹妹穿的比我還像新娘。我一直安慰自己抄伍,他們只是感情好艘刚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著截珍,像睡著了一般攀甚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上岗喉,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天秋度,我揣著相機(jī)與錄音,去河邊找鬼钱床。 笑死荚斯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的诞丽。 我是一名探鬼主播鲸拥,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拐格,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼僧免!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起捏浊,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤懂衩,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浊洞,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡牵敷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了法希。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枷餐。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖苫亦,靈堂內(nèi)的尸體忽然破棺而出毛肋,到底是詐尸還是另有隱情,我是刑警寧澤屋剑,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布润匙,位于F島的核電站,受9級(jí)特大地震影響唉匾,放射性物質(zhì)發(fā)生泄漏孕讳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一巍膘、第九天 我趴在偏房一處隱蔽的房頂上張望厂财。 院中可真熱鬧,春花似錦峡懈、人聲如沸蟀苛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)帜平。三九已至,卻和暖如春梅鹦,著一層夾襖步出監(jiān)牢的瞬間裆甩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工齐唆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗤栓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓箍邮,卻偏偏與公主長(zhǎng)得像茉帅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锭弊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容