Code Playground

Open in CodeSandbox
/*
  This file shows how we can bounce a ball back and forth using JavaScript, inside a requestAnimationFrame loop.

  This is done purely for hypothetical purposes, to evaluate the performance of doing animations in JS. For this particular problem, it makes way more sense to use CSS keyframe animations!

  The strategy here is to keep track of how much time has passed since the animation started. That way, we can work out mathematically where the ball should be for the current frame. The benefit of this approach is that the animation will always be in sync; even if a few frames are dropped, the ball will jump to the correct location, instead of falling behind schedule.
*/
import './reset.css';
import './styles.css';
import { normalize } from './utils';

const BOUNCE_MAGNITUDE = 128;
const BOUNCE_TIME = 1000;

const ball = document.querySelector('.ball');

const startAt = performance.now();

function animate() {
  const totalTimeElapsed = performance.now() - startAt;

  // Get the number of times the ball has bounced
  const bounceIndex = Math.floor(totalTimeElapsed / BOUNCE_TIME);

  // When bounceIndex is a multiple of 2, it should move to the
  // right. Otherwise, it should move to the left.
  const bounceDirection = bounceIndex % 2 === 0 ? 'going-right' : 'going-left';

  // How far are we into the current bounce?
  const bounceProgress = totalTimeElapsed % BOUNCE_TIME;

  // Finally, we need to translate `bounceProgress` (which is a
  // value between 0 and BOUNCE_MAGNITUDE) into an actual pixel
  // value, to apply the CSS transform. We’ll use our trusty
  // `normalize` function for this.
  // If the ball is moving to the left, we need to flip the min/max,
  // since the ball STARTS at `BOUNCE_MAGNITUDE` and moves to 0.
  const x =
    bounceDirection === 'going-right'
      ? normalize(
          bounceProgress,
          0,
          BOUNCE_TIME,
          0,
          BOUNCE_MAGNITUDE
        )
      : normalize(
          bounceProgress,
          0,
          BOUNCE_TIME,
          BOUNCE_MAGNITUDE,
          0
        );

  ball.style.transform = `translateX(${x}px)`;

  requestAnimationFrame(animate);
}

animate();