欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

requestAnimationFrame用法優(yōu)化源碼解析

 更新時(shí)間:2023年10月24日 09:04:45   作者:千年老妖  
這篇文章主要介紹了requestAnimationFrame用法優(yōu)化源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

介紹

什么是requestAnimationFrame

requestAnimationFrame是瀏覽器用于定時(shí)循環(huán)操作的一個(gè)API,通常用于動(dòng)畫(huà)和游戲開(kāi)發(fā)。它會(huì)把每一幀中的所有DOM操作集中起來(lái),在重繪之前一次性更新,并且關(guān)聯(lián)到瀏覽器的重繪操作。

為什么使用requestAnimationFrame而不是setTimeout或setInterval

setTimeoutsetInterval相比,requestAnimationFrame具有以下優(yōu)勢(shì):

  • 通過(guò)系統(tǒng)時(shí)間間隔來(lái)調(diào)用回調(diào)函數(shù),無(wú)需擔(dān)心系統(tǒng)負(fù)載和阻塞問(wèn)題,系統(tǒng)會(huì)自動(dòng)調(diào)整回調(diào)頻率。
  • 由瀏覽器內(nèi)部進(jìn)行調(diào)度和優(yōu)化,性能更高,消耗的CPU和GPU資源更少。
  • 避免幀丟失現(xiàn)象,確保回調(diào)連續(xù)執(zhí)行,實(shí)現(xiàn)更流暢的動(dòng)畫(huà)效果。
  • 自動(dòng)合并多個(gè)回調(diào),避免不必要的開(kāi)銷(xiāo)。
  • 與瀏覽器的刷新同步,不會(huì)在瀏覽器頁(yè)面不可見(jiàn)時(shí)執(zhí)行回調(diào)。

requestAnimationFrame的優(yōu)勢(shì)和適用場(chǎng)景

requestAnimationFrame最適用于需要連續(xù)高頻執(zhí)行的動(dòng)畫(huà),如游戲開(kāi)發(fā),數(shù)據(jù)可視化動(dòng)畫(huà)等。它與瀏覽器刷新周期保持一致,不會(huì)因?yàn)殚g隔時(shí)間不均勻而導(dǎo)致動(dòng)畫(huà)卡頓。

使用方法

requestAnimationFrame的基本語(yǔ)法

requestAnimationFrame接收一個(gè)回調(diào)函數(shù)作為參數(shù),該回調(diào)函數(shù)會(huì)在瀏覽器下一次重繪前執(zhí)行。

const animation = () => {
  // 執(zhí)行動(dòng)畫(huà)
  requestAnimationFrame(animation); 
}
requestAnimationFrame(animation);

上面代碼會(huì)不停調(diào)用requestAnimationFrame,在每次瀏覽器重繪前執(zhí)行回調(diào)函數(shù),實(shí)現(xiàn)連續(xù)動(dòng)畫(huà)效果。

如何在動(dòng)畫(huà)中使用requestAnimationFrame

可以在回調(diào)函數(shù)里更新動(dòng)畫(huà)的狀態(tài),然后清除上一幀,繪制新?tīng)顟B(tài)的這一幀:

let angle = 0; 
const render = () => {
  ctx.clearRect(0, 0, width, height); // 清除上一幀
  // 更新?tīng)顟B(tài)
  angle += delta;
  ctx.beginPath();
  ctx.arc(x, y, radius, 0, angle); 
  ctx.stroke();
  requestAnimationFrame(render);
}

這樣通過(guò)在每次回調(diào)中更新參數(shù),就可以實(shí)現(xiàn)對(duì)象的連續(xù)動(dòng)畫(huà)了。

requestAnimationFrame的回調(diào)函數(shù)參數(shù)

requestAnimationFrame的回調(diào)函數(shù)會(huì)收到一個(gè)參數(shù),這個(gè)參數(shù)是一個(gè)時(shí)間戳,單位為毫秒,代表requestAnimationFrame被觸發(fā)的時(shí)間??梢愿鶕?jù)這個(gè)時(shí)間戳計(jì)算兩幀的時(shí)間間隔,來(lái)調(diào)整動(dòng)畫(huà)速度。

let prevTimestamp;
const render = timestamp => {
  if (!prevTimestamp) prevTimestamp = timestamp;
  const delta = timestamp - prevTimestamp;
  // 根據(jù)時(shí)間間隔計(jì)算速度
  x += speed * delta;
  prevTimestamp = timestamp;
  requestAnimationFrame(render);
}

性能優(yōu)化

避免在requestAnimationFrame回調(diào)函數(shù)中進(jìn)行大量計(jì)算

由于requestAnimationFrame的回調(diào)會(huì)在一個(gè)高優(yōu)先級(jí)的線程中被同步執(zhí)行,如果回調(diào)函數(shù)中有大量計(jì)算,會(huì)導(dǎo)致此線程被阻塞,從而引起頁(yè)面卡頓。

也就是如果 requestAnimationFrame 的回調(diào)函數(shù)執(zhí)行時(shí)間超過(guò)一幀(通常是 16.67 毫秒,因?yàn)闉g覽器通常以每秒約 60 幀的速度刷新),則可能會(huì)導(dǎo)致動(dòng)畫(huà)性能下降,可能出現(xiàn)掉幀的情況,最終影響用戶體驗(yàn)。這是因?yàn)闉g覽器每幀的時(shí)間是有限的,如果回調(diào)函數(shù)執(zhí)行時(shí)間過(guò)長(zhǎng),就會(huì)導(dǎo)致下一幀的準(zhǔn)備和繪制時(shí)間受到壓縮,導(dǎo)致動(dòng)畫(huà)卡頓。

通常,應(yīng)該盡量避免在 requestAnimationFrame 的回調(diào)函數(shù)中執(zhí)行耗時(shí)的操作。為了解決這個(gè)問(wèn)題,可以采取以下一些策略:

  • 優(yōu)化回調(diào)函數(shù): 使回調(diào)函數(shù)盡可能簡(jiǎn)短,避免不必要的計(jì)算或循環(huán)。在回調(diào)中只執(zhí)行與動(dòng)畫(huà)相關(guān)的必要操作。
  • 分幀處理: 如果動(dòng)畫(huà)需要處理大量數(shù)據(jù)或計(jì)算復(fù)雜的操作,可以將這些操作分散到多個(gè) requestAnimationFrame 回調(diào)中,以避免長(zhǎng)時(shí)間的占用。
  • Web Workers: 將耗時(shí)的計(jì)算放在獨(dú)立的 Web Worker 線程中執(zhí)行,以不影響主線程和動(dòng)畫(huà)渲染。
  • 幀率控制: 如果回調(diào)函數(shù)耗時(shí)較長(zhǎng),可以根據(jù)回調(diào)函數(shù)的實(shí)際執(zhí)行時(shí)間來(lái)控制動(dòng)畫(huà)的幀率。減小動(dòng)畫(huà)對(duì)象的速度或者減少渲染質(zhì)量,以適應(yīng)當(dāng)前性能。
  • 監(jiān)測(cè)性能: 使用瀏覽器開(kāi)發(fā)者工具來(lái)監(jiān)測(cè)性能,以找出哪些操作導(dǎo)致了回調(diào)函數(shù)執(zhí)行時(shí)間過(guò)長(zhǎng)。

總之,確保 requestAnimationFrame 回調(diào)函數(shù)的執(zhí)行時(shí)間盡量短,以確保動(dòng)畫(huà)的流暢性和性能。

使用硬件加速優(yōu)化動(dòng)畫(huà)性能

啟用GPU加速渲染,可以顯著提升動(dòng)畫(huà)性能。

.animated {
  transform: translateZ(0); /* 開(kāi)啟硬件加速 */  
}

如何在不同設(shè)備上實(shí)現(xiàn)平滑的動(dòng)畫(huà)效果

可根據(jù)requestAnimationFrame回調(diào)的時(shí)間戳,計(jì)算這一幀與上一幀的間隔時(shí)間delta,并根據(jù)delta的值采取不同的優(yōu)化手段:

  • delta特別小,說(shuō)明這一幀花費(fèi)時(shí)間過(guò)長(zhǎng),可能導(dǎo)致掉幀,可以降低動(dòng)畫(huà)對(duì)象的移動(dòng)速度或圖像質(zhì)量
  • delta逐漸變大,說(shuō)明動(dòng)畫(huà)逐漸卡頓,可以降低動(dòng)畫(huà)對(duì)象數(shù)量或復(fù)雜度
  • delta波動(dòng)較大,說(shuō)明系統(tǒng)資源不足,可以采用簡(jiǎn)單的動(dòng)畫(huà)作為降級(jí)策略

與其他動(dòng)畫(huà)庫(kù)的比較

requestAnimationFrame與setTimeout/setInterval的區(qū)別

  • setTimeout/setInterval是固定時(shí)間間隔觸發(fā), requestAnimationFrame依賴(lài)系統(tǒng)刷新調(diào)度
  • setTimeout/setInterval會(huì)無(wú)視頁(yè)面是否可見(jiàn), requestAnimationFrame會(huì)停止刷新
  • setTimeout/setInterval難以避免丟幀問(wèn)題,requestAnimationFrame與刷新同步避免丟幀

requestAnimationFrame與CSS動(dòng)畫(huà)的結(jié)合使用

requestAnimationFrame可用于更新動(dòng)畫(huà)狀態(tài),實(shí)現(xiàn)復(fù)雜動(dòng)畫(huà)邏輯,而CSS動(dòng)畫(huà)用于聲明式定義動(dòng)畫(huà)的樣式變化,兩者可配合實(shí)現(xiàn)更豐富的動(dòng)畫(huà)效果。

實(shí)際應(yīng)用

使用requestAnimationFrame實(shí)現(xiàn)常見(jiàn)動(dòng)畫(huà)效果

可使用 requestAnimationFrame 實(shí)現(xiàn)對(duì)象軌跡動(dòng)畫(huà)、SVG圖形動(dòng)畫(huà)、加載動(dòng)畫(huà)等效果。

// 小球拖尾效果
const positions = [];
const render = () => {
  // 添加新位置
  positions.push({x, y});
  if (positions.length > 10){
    positions.shift(); 
  }
  // 渲染小球
  ctx.clearRect(0, 0, width, height);
  positions.forEach(pos => ctx.fillRect(pos.x, pos.y, 10, 10));
  requestAnimationFrame(render);
}
// SVG繪制動(dòng)畫(huà)
let length = 0;
const render = () => {
  length += 4;
  svgLine.setAttribute("stroke-dasharray", length);
  if (length < 300) {
    requestAnimationFrame(render);
  }
}
requestAnimationFrame(render); 
// 進(jìn)度條加載動(dòng)畫(huà)  
let progress = 0;
const render = () => {
  progress += 1; 
  loadBar.style.width = progress + '%';
  if(progress < 100) {
    requestAnimationFrame(render);
  }
}
requestAnimationFrame(render);

requestAnimationFrame在游戲開(kāi)發(fā)中的應(yīng)用

游戲需要非常流暢的畫(huà)面和實(shí)時(shí)的響應(yīng),這正是requestAnimationFrame的優(yōu)勢(shì),它可用于實(shí)現(xiàn)游戲中的物體移動(dòng)、碰撞檢測(cè)、幀數(shù)控制等操作。

// 飛機(jī)射擊動(dòng)畫(huà)
const update = () => {
  // 子彈位置更新
  bullets.forEach(bullet => {
    bullet.position += speed;
  })
  // 飛機(jī)位置更新
  aircraft.position += delta * speed;
  // 繪制所有元素
  render(bullets, aircraft);
  requestAnimationFrame(update);
}

requestAnimationFrame在響應(yīng)式設(shè)計(jì)中的應(yīng)用

可使用requestAnimationFrame來(lái)更平滑地執(zhí)行響應(yīng)式布局的變化,避免布局突然大幅移動(dòng)帶來(lái)的視覺(jué)沖擊感。

let width = 500;
const resize = () => {
  width = container.clientWidth;
  box.style.width = width + "px";
  requestAnimationFrame(resize);
}
window.addEventListener("resize", resize);

兼容性和后續(xù)發(fā)展

requestAnimationFrame的瀏覽器兼容性

requestAnimationFrame現(xiàn)在已經(jīng)得到了廣泛支持,可以直接使用。對(duì)于不支持的瀏覽器,可以用setTimeout模擬requestAnimationFrame。

window.requestAnimationFrame = window.requestAnimationFrame ||
                               window.webkitRequestAnimationFrame ||
                               (cb => setTimeout(cb, 1000/60));

requestAnimationFrame的未來(lái)發(fā)展趨勢(shì)

未來(lái)requestAnimationFrame可能會(huì)支持設(shè)置幀率、增強(qiáng)調(diào)度算法等,提升動(dòng)畫(huà)性能。Web工作者線程也可帶來(lái)更多優(yōu)化空間。

瀏覽器廠商也在繼續(xù)改進(jìn)相關(guān)API,比如setTimeoutrequestIdleCallback也在朝著更精確的調(diào)度方向發(fā)展。

如何在不支持requestAnimationFrame的瀏覽器中實(shí)現(xiàn)類(lèi)似效果

可以通過(guò)自己實(shí)現(xiàn)一個(gè)polyfill:

window.requestAnimationFrame = window.requestAnimationFrame || 
                               window.webkitRequestAnimationFrame ||
                               (cb => setTimeout(cb, 1000/60));

這樣就可以在大多數(shù)瀏覽器中使用 requestAnimationFrame 了。對(duì)于老舊瀏覽器,可以使用 setInterval 模擬,但效果會(huì)比較粗糙。

總結(jié)

requestAnimationFrame是實(shí)現(xiàn)復(fù)雜動(dòng)畫(huà)的好幫手,必須掌握其用法與優(yōu)化技巧,才能發(fā)揮其最大效用。同時(shí)結(jié)合其他技術(shù)如CSS動(dòng)畫(huà)、Web Worker等也可以實(shí)現(xiàn)更好的性能。隨著瀏覽器的不斷進(jìn)步,requestAnimationFrame還具有很大的拓展?jié)摿Α?/p>

以上就是requestAnimationFrame用法優(yōu)化源碼解析的詳細(xì)內(nèi)容,更多關(guān)于requestAnimationFrame優(yōu)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論