Home > Web Front-end > JS Tutorial > body text

Supercharge Your Web Animations: Optimize requestAnimationFrame Like a Pro

DDD
Release: 2024-09-26 22:23:30
Original
208 people have browsed it

Supercharge Your Web Animations: Optimize requestAnimationFrame Like a Pro

流暢且高效能的動畫在現代 Web 應用程式中至關重要。然而,管理不當可能會使瀏覽器的主執行緒過載,導致效能不佳和動畫卡頓。 requestAnimationFrame (rAF) 是一種瀏覽器 API,旨在將動畫與顯示器的刷新率同步,從而確保與 setTimeout 等替代方案相比更流暢的運動。但高效使用 rAF 需要仔細規劃,尤其是在處理多個動畫時。

在本文中,我們將探討如何透過集中動畫管理、引入 FPS 控制以及保持瀏覽器主執行緒回應來最佳化 requestAnimationFrame。


了解 FPS 及其重要性

在討論動畫效能時,每秒影格數 (FPS) 至關重要。大多數螢幕以 60 FPS 刷新,這意味著 requestAnimationFrame 每秒被呼叫 60 次。為了保持流暢的動畫,瀏覽器必須在每幀約 16.67 毫秒內完成其工作。

如果在單一影格中執行太多任務,瀏覽器可能會錯過其目標幀時間,從而導致卡頓或丟幀。降低某些動畫的 FPS 有助於減少主執行緒的負載,從而在效能和流暢度之間取得平衡。

具有 FPS 控制功能的集中式動畫管理器可實現更好的效能

為了更有效地管理動畫,我們可以透過共享循環集中處理動畫,而不是在程式碼中分散多個 requestAnimationFrame 呼叫。集中式方法可最大程度地減少冗餘調用,並更輕鬆地添加 FPS 控制。

下面的AnimationManager類別允許我們在控制目標FPS的同時註冊和取消註冊動畫任務。預設情況下,我們的目標是 60 FPS,但這可以根據效能需求進行調整。

class AnimationManager {
  private tasks: Set<FrameRequestCallback> = new Set();
  private fps: number = 60; // Target FPS
  private lastFrameTime: number = performance.now();
  private animationId: number | null = null; // Store the animation frame ID

  private run = (currentTime: number) => {
    const deltaTime = currentTime - this.lastFrameTime;

    // Ensure the tasks only run if enough time has passed to meet the target FPS
    if (deltaTime > 1000 / this.fps) {
      this.tasks.forEach((task) => task(currentTime));
      this.lastFrameTime = currentTime;
    }

    this.animationId = requestAnimationFrame(this.run);
  };

  public registerTask(task: FrameRequestCallback) {
    this.tasks.add(task);
    if (this.tasks.size === 1) {
      this.animationId = requestAnimationFrame(this.run); // Start the loop if this is the first task
    }
  }

  public unregisterTask(task: FrameRequestCallback) {
    this.tasks.delete(task);
    if (this.tasks.size === 0 && this.animationId !== null) {
      cancelAnimationFrame(this.animationId); // Stop the loop if no tasks remain
      this.animationId = null; // Reset the ID
    }
  }
}

export const animationManager = new AnimationManager();
Copy after login

在此設定中,我們計算幀之間的 deltaTime,以確定基於目標 FPS 是否已經過去了足夠的時間進行下一次更新。這使我們能夠限制更新頻率,以確保瀏覽器的主執行緒不會過載。


實際範例:為具有不同屬性的多個元素設定動畫

讓我們建立一個範例,其中我們為三個盒子設定動畫,每個盒子都有不同的動畫:一個縮放,另一個改變顏色,第三個旋轉。

這是 HTML:

<div id="animate-box-1" class="animated-box"></div>
<div id="animate-box-2" class="animated-box"></div>
<div id="animate-box-3" class="animated-box"></div>
Copy after login

這是 CSS:

.animated-box {
  width: 100px;
  height: 100px;
  background-color: #3498db;
  transition: transform 0.1s ease;
}
Copy after login

現在,我們將新增 JavaScript 來為每個具有不同屬性的方塊設定動畫。一個會縮放,另一個會改變顏色,第三個會旋轉。

第 1 步:新增線性內插 (lerp)

線性插值 (lerp) 是動畫中常用的技術,用於在兩個值之間平滑過渡。它有助於創建漸進且平滑的進程,使其成為隨時間推移縮放、移動或更改屬性的理想選擇。此函數採用三個參數:起始值、結束值和標準化時間 (t),該時間決定過渡的距離。

function lerp(start: number, end: number, t: number): number {
  return start + (end - start) * t;
}
Copy after login

第 2 步:縮放動畫

我們先建立一個函數來為第一個框的縮放設定動畫:

function animateScale(
  scaleBox: HTMLDivElement,
  startScale: number,
  endScale: number,
  speed: number
) {
  let scaleT = 0;

  function scale() {
    scaleT += speed;
    if (scaleT > 1) scaleT = 1;

    const currentScale = lerp(startScale, endScale, scaleT);
    scaleBox.style.transform = `scale(${currentScale})`;

    if (scaleT === 1) {
      animationManager.unregisterTask(scale);
    }
  }

  animationManager.registerTask(scale);
}
Copy after login

第 3 步:彩色動畫

接下來,我們為第二個框的顏色變化設定動畫:

function animateColor(
  colorBox: HTMLDivElement,
  startColor: number,
  endColor: number,
  speed: number
) {
  let colorT = 0;

  function color() {
    colorT += speed;
    if (colorT > 1) colorT = 1;

    const currentColor = Math.floor(lerp(startColor, endColor, colorT));
    colorBox.style.backgroundColor = `rgb(${currentColor}, 100, 100)`;

    if (colorT === 1) {
      animationManager.unregisterTask(color);
    }
  }

  animationManager.registerTask(color);
}
Copy after login

第 4 步:旋轉動畫

最後,我們建立旋轉第三個框的函數:

function animateRotation(
  rotateBox: HTMLDivElement,
  startRotation: number,
  endRotation: number,
  speed: number
) {
  let rotationT = 0;

  function rotate() {
    rotationT += speed; // Increment progress
    if (rotationT > 1) rotationT = 1;

    const currentRotation = lerp(startRotation, endRotation, rotationT);
    rotateBox.style.transform = `rotate(${currentRotation}deg)`;

    // Unregister task once the animation completes
    if (rotationT === 1) {
      animationManager.unregisterTask(rotate);
    }
  }

  animationManager.registerTask(rotate);
}
Copy after login

第 5 步:開始動畫

最後,我們可以啟動所有三個盒子的動畫:

// Selecting the elements
const scaleBox = document.querySelector("#animate-box-1") as HTMLDivElement;
const colorBox = document.querySelector("#animate-box-2") as HTMLDivElement;
const rotateBox = document.querySelector("#animate-box-3") as HTMLDivElement;

// Starting the animations
animateScale(scaleBox, 1, 1.5, 0.02); // Scaling animation
animateColor(colorBox, 0, 255, 0.01); // Color change animation
animateRotation(rotateBox, 360, 1, 0.005); // Rotation animation
Copy after login

主線程注意事項

使用 requestAnimationFrame 時,必須記住動畫在瀏覽器的主執行緒上運行。主執行緒超載過多的任務可能會導致瀏覽器錯過動畫幀,從而導致卡頓。這就是為什麼使用集中式動畫管理器和 FPS 控制等工具來最佳化動畫可以幫助保持流暢度,即使有多個動畫也是如此。


結論

在 JavaScript 中有效管理動畫需要的不僅僅是使用 requestAnimationFrame。透過集中動畫並控制 FPS,您可以確保動畫更流暢、效能更高,同時保持主執行緒回應能力。在此範例中,我們展示如何使用單一 AnimationManager 處理多個動畫,示範如何最佳化效能和可用性。雖然為了簡單起見,我們專注於保持一致的 FPS,但這種方法可以擴展到處理各種動畫的不同 FPS 值,儘管這超出了本文的範圍。

Github Repo: https://github.com/JBassx/rAF-optimization
StackBlitz: https://stackblitz.com/~/github.com/JBassx/rAF-optimization

LinkedIn: https://www.linkedin.com/in/josephciullo/

The above is the detailed content of Supercharge Your Web Animations: Optimize requestAnimationFrame Like a Pro. For more information, please follow other related articles on the PHP Chinese website!

source:dev.to
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!