Optimizing Animation Performance with requestAnimationFrame

Optimizing Animation Performance with requestAnimationFrame

Problem Description
requestAnimationFrame is an API provided by browsers specifically designed for optimizing animation effects. Compared to traditional setTimeout/setInterval, it ensures that animation frame rates synchronize with the browser's refresh rate, avoiding excessive rendering and stuttering. Please explain in detail how it works, its performance advantages, and specific implementation methods.

Solution Process

  1. Performance Issues with Traditional Animation Implementation

    • setTimeout/setInterval executes callbacks at fixed time intervals but cannot guarantee synchronization with screen refreshes.
    • Typical problems:
      • Dropped frames: setTimeout(callback, 16.7ms) may experience actual intervals > 16.7ms due to task blocking (each frame at 60Hz screen refresh rate takes 16.7ms).
      • Excessive rendering: Intervals < 16.7ms can cause multiple renders within a single frame, wasting resources.
    • Example code defects:
      // Problematic case: May drop frames or over-render
      setInterval(() => {
          element.style.left = (parseInt(element.style.left) + 1) + 'px';
      }, 16);
      
  2. Core Mechanism of requestAnimationFrame

    • The browser automatically calls the callback function before the next repaint.
    • Intelligent pausing: Automatically pauses animations when the page is hidden, reducing CPU/GPU consumption.
    • Adaptive frame rate: Defaults to matching the display refresh rate (e.g., 60Hz/120Hz).
    • Basic usage:
      function animate() {
          // Update animation state
          element.style.transform = `translateX(${position}px)`;
      
          // Recursive call
          requestAnimationFrame(animate);
      }
      requestAnimationFrame(animate);
      
  3. Time Control for Uniform-Speed Animation

    • Problem: Directly modifying properties may cause animation speed to vary with frame rate.
    • Solution: Calculate displacement based on time difference.
      let startTime;
      function animate(timestamp) {
          if (!startTime) startTime = timestamp;
          // Calculate the proportion of elapsed time to total animation duration
          const progress = (timestamp - startTime) / 2000; // Complete in 2 seconds
      
          // Update element state (example: horizontal movement of 300px)
          element.style.transform = `translateX(${progress * 300}px)`;
      
          if (progress < 1) {
              requestAnimationFrame(animate);
          }
      }
      
  4. Optimizing Batch DOM Operations

    • Combine multiple style modifications within a single frame:
      function updateFrames() {
          // Read all layout properties before triggering reflow
          const width = element.offsetWidth;
      
          // Apply style changes collectively (browser intelligently merges repaints)
          element.style.cssText = `
              width: ${width * 2}px;
              background: #f00;
              transform: scale(1.5);
          `;
      
          requestAnimationFrame(nextStage);
      }
      
  5. Synergistic Optimization with CSS Animations

    • Complex animations are recommended to be implemented with CSS (browsers offer deeper optimization).
    • JavaScript only controls animation states:
      .box {
          transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.9, 1);
      }
      
      // Trigger CSS animations by toggling class names
      element.classList.add('animated');
      // Use rAF to ensure style changes are executed at the optimal time
      requestAnimationFrame(() => {
          element.style.transform = 'translateX(200px)';
      });
      
  6. Advanced Application: Animation Queue Management

    • Use cancelAnimationFrame to avoid conflicts:
      let animationId;
      function startAnimation() {
          // Cancel existing animations
          if (animationId) cancelAnimationFrame(animationId);
      
          function step(timestamp) {
              // Animation logic...
              animationId = requestAnimationFrame(step);
          }
          animationId = requestAnimationFrame(step);
      }
      

Performance Comparison Summary

  • Compared to setInterval: Reduces power consumption by 40% (based on browser optimization data).
  • Frame rate stability: Achieves 59-60fps (setInterval typically fluctuates between 50-60fps).
  • Memory usage: Automatic garbage collection mechanism prevents memory leaks.

By adopting a layered usage strategy (CSS animations for simple scenarios, rAF for controlling complex logic), cinema-level smooth animation effects can be achieved.