A brief analysis of the usage and optimization of requestAnimationFrame

Introduction

What is requestAnimationFrame

requestAnimationFrame is an API used by browsers for timed loop operations, usually used for animation and game development. It will centralize all DOM operations in each frame, update them all at once before redrawing, and associate them with the browser’s redraw operation.

Why use requestAnimationFrame instead of setTimeout or setInterval

Compared with setTimeout or setInterval, requestAnimationFrame has the following advantages:

  • Call the callback function through the system time interval. There is no need to worry about system load and blocking issues. The system will automatically adjust the callback frequency.
  • Scheduled and optimized internally by the browser, the performance is higher and consumes less CPU and GPU resources.
  • Avoid frame loss, ensure continuous execution of callbacks, and achieve smoother animation effects.
  • Automatically merge multiple callbacks to avoid unnecessary overhead.
  • Synchronized with browser refresh, no callback will be executed when the browser page is not visible.

Advantages and applicable scenarios of requestAnimationFrame

requestAnimationFrame is most suitable for animations that require continuous high-frequency execution, such as game development, data visualization animation, etc. It is consistent with the browser refresh cycle and will not cause animation freezes due to uneven intervals.

How to use

Basic syntax of requestAnimationFrame

requestAnimationFrame receives a callback function as a parameter, which will be executed before the next redraw of the browser.

const animation = () => {
  //Execute animation
  requestAnimationFrame(animation);
}

requestAnimationFrame(animation);

The above code will continuously call requestAnimationFrame and execute the callback function before each browser redraw to achieve continuous animation effects.

How to use requestAnimationFrame in animation

You can update the state of the animation in the callback function, then clear the previous frame and draw the frame in the new state:

let angle = 0;

const render = () => {
  ctx.clearRect(0, 0, width, height); // Clear the previous frame

  // update status
  angle + = delta;
  
  ctx.beginPath();
  ctx.arc(x, y, radius, 0, angle);
  ctx.stroke();

  requestAnimationFrame(render);
}

In this way, by updating parameters in each callback, continuous animation of the object can be achieved.

requestAnimationFrame callback function parameters

The callback function of requestAnimationFrame will receive a parameter, which is a timestamp in milliseconds, representing the time when requestAnimationFrame was triggered. The time interval between two frames can be calculated based on this timestamp to adjust the animation speed.

let prevTimestamp;
const render = timestamp => {
  if (!prevTimestamp) prevTimestamp = timestamp;
  const delta = timestamp - prevTimestamp;

  // Calculate speed based on time interval
  x + = speed * delta;

  prevTimestamp = timestamp;
  requestAnimationFrame(render);
}

Performance optimization

Avoid a large number of calculations in the requestAnimationFrame callback function

Since the callback of requestAnimationFrame will be executed synchronously in a high-priority thread, if there are a lot of calculations in the callback function, this thread will be blocked, causing the page to freeze.

That is, if the execution time of requestAnimationFrame‘s callback function exceeds one frame (usually 16.67 milliseconds, because the browser usually refreshes at about 60 frames per second), it may cause animation performance to decrease, and may occur Frame drops ultimately affect the user experience. This is because the time of each frame of the browser is limited. If the execution time of the callback function is too long, the preparation and drawing time of the next frame will be compressed, causing the animation to freeze.

In general, you should try to avoid performing time-consuming operations in the callback function of requestAnimationFrame. To solve this problem, here are some strategies you can adopt:

  1. Optimize callback functions: Make callback functions as short as possible to avoid unnecessary calculations or loops. Only perform necessary animation-related operations in the callback.

  2. Frame processing: If the animation requires processing a large amount of data or computationally complex operations, these operations can be spread out into multiple requestAnimationFrame callbacks to avoid long-term occupancy.

  3. Web Workers: Execute time-consuming calculations in a separate Web Worker thread so as not to affect the main thread and animation rendering.

  4. Frame rate control: If the callback function takes a long time, you can control the frame rate of the animation based on the actual execution time of the callback function. Reduce the speed of animated objects or reduce rendering quality to accommodate current performance.

  5. Monitor performance: Use browser developer tools to monitor performance to find out which operations are causing callback functions to take too long to execute.

In summary, ensure that the execution time of the requestAnimationFrame callback function is as short as possible to ensure smooth animation and performance.

Use hardware acceleration to optimize animation performance

Enabling GPU accelerated rendering can significantly improve animation performance.

.animated {
  transform: translateZ(0); /* Enable hardware acceleration */
}

How to achieve smooth animation effects on different devices

You can calculate the interval delta between this frame and the previous frame based on the timestamp of the requestAnimationFrame callback, and adopt different methods based on the value of delta Optimization means:

  • delta is extremely small, indicating that this frame takes too long, which may cause frame drops and reduce the moving speed or image quality of animated objects.
  • delta gradually becomes larger, indicating that the animation gradually becomes stuck, which can reduce the number or complexity of animation objects.
  • delta fluctuates greatly, indicating insufficient system resources. Simple animation can be used as a downgrade strategy.

Comparison with other animation libraries

The difference between requestAnimationFrame and setTimeout/setInterval

  • setTimeout/setInterval is triggered at a fixed time interval, requestAnimationFrame relies on system refresh scheduling
  • setTimeout/setInterval will ignore whether the page is visible, requestAnimationFrame will stop refreshing
  • setTimeout/setInterval is difficult to avoid frame loss. requestAnimationFrame is synchronized with refresh to avoid frame loss.

Combined use of requestAnimationFrame and CSS animation

requestAnimationFrame can be used to update animation status and implement complex animation logic, while CSS animation is used to declaratively define animation style changes. The two can be used together to achieve richer animation effects.

Practical application

Use requestAnimationFrame to achieve common animation effects

You can use requestAnimationFrame to achieve effects such as object trajectory animation, SVG graphic animation, and loading animation.

// Ball tailing effect
const positions = [];

const render = () => {
  //Add new location
  positions.push({x, y});

  if (positions.length > 10){
    positions.shift();
  }

  // Render the ball
  ctx.clearRect(0, 0, width, height);
  positions.forEach(pos => ctx.fillRect(pos.x, pos.y, 10, 10));

  requestAnimationFrame(render);
}
// SVG drawing animation
let length = 0;

const render = () => {
  length + = 4;

  svgLine.setAttribute("stroke-dasharray", length);

  if (length < 300) {
    requestAnimationFrame(render);
  }
}

requestAnimationFrame(render);
// Progress bar loading animation
let progress = 0;

const render = () => {
  progress + = 1;
  loadBar.style.width = progress + '%';

  if(progress < 100) {
    requestAnimationFrame(render);
  }
}

requestAnimationFrame(render);

Application of requestAnimationFrame in game development

Games require very smooth graphics and real-time response. This is the advantage of requestAnimationFrame. It can be used to implement operations such as object movement, collision detection, and frame number control in the game.

//Aircraft shooting animation
const update = () => {
  // Bullet position update
  bullets.forEach(bullet => {
    bullet.position + = speed;
  })

  // Aircraft position update
  aircraft.position + = delta * speed;

  // Draw all elements
  render(bullets, aircraft);
  
  requestAnimationFrame(update);
}

Application of requestAnimationFrame in responsive design

You can use requestAnimationFrame to perform responsive layout changes more smoothly and avoid the visual impact caused by sudden large movements of the layout.

let width = 500;

const resize = () => {
  width = container.clientWidth;

  box.style.width = width + "px";

  requestAnimationFrame(resize);
}

window.addEventListener("resize", resize);

Compatibility and subsequent development

Browser compatibility of requestAnimationFrame

requestAnimationFrame is now widely supported and can be used directly. For browsers that do not support it, you can use setTimeout to simulate requestAnimationFrame.

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

The future development trend of requestAnimationFrame

In the future, requestAnimationFrame may support setting frame rates, enhancing scheduling algorithms, etc. to improve animation performance. Web worker threads also bring more room for optimization.

Browser manufacturers are also continuing to improve related APIs, such as setTimeout and requestIdleCallback, which are also moving towards more precise scheduling.

How to achieve similar effects in browsers that do not support requestAnimationFrame

You can implement a polyfill yourself:

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

This will allow you to use requestAnimationFrame in most browsers. For older browsers, you can use setInterval to simulate, but the effect will be rough.

Summary

requestAnimationFrame is a good helper for implementing complex animations. You must master its usage and optimization skills to maximize its effectiveness. At the same time, better performance can also be achieved by combining other technologies such as CSS animation, Web Worker, etc. With the continuous advancement of browsers, requestAnimationFrame also has great potential for expansion.

This article was first published on the public account “Front-End Dashi Brother”. Please indicate this information when reprinting.