3
\$\begingroup\$

I've created a tiny (723 bytes compressed + gzip) animation library. Is this a good way of animating arbitrary numbers?

const map = (value, x1, x2, y1, y2) => (value - x1) * (y2 - y1) / (x2 - x1) + y1;
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);

// Courtesy of @gre from https://gist.github.com/gre/1650294
const Easing = {
  // no easing, no acceleration
  linear: t => t,
  // acceleration until halfway, then deceleration
  easeInOutQuad: t => t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
};

function Animation(settings) {
  if (typeof settings !== 'object' || (typeof settings === 'object' && settings instanceof Array)) {
    throw new Error('The settings passed to Animation must be an object');
  }

  if (!settings.hasOwnProperty('duration')) {
    throw new Error('The settings passed to Animation must have a "duration" property set');
  }

  if (!settings.hasOwnProperty('from')) {
    throw new Error('The settings passed to Animation must have a "from" property set');
  }

  if (!settings.hasOwnProperty('to')) {
    throw new Error('The settings passed to Animation must have a "to" property set');
  }

  if (settings.hasOwnProperty('easing') && typeof settings.easing !== 'function') {
    throw new Error('Invalid easing function passed to settings. Easing function must be one of the functions provided in the Easing object, e.g. Easing.linear');
  }

  const duration = settings.duration;
  const easing = settings.easing || Easing.linear;
  const timestamps = {
    start: 0,
    finish: 0,
  };
  const value = {
    from: settings.from,
    to: settings.to,
  };

  let nextFrame, tickCallback;

  const setTimestamps = currentTimestamp => {
    timestamps.start = currentTimestamp;
    timestamps.finish = currentTimestamp + duration;
  };

  const tick = () => {
    nextFrame = requestAnimationFrame(tick);

    const currentTimestamp = Date.now();

    if (timestamps.start + timestamps.finish === 0) {
      setTimestamps(currentTimestamp);
    }

    const normal = easing(map(currentTimestamp, timestamps.start, timestamps.finish, 0, 1));
    const delta = clamp(normal, 0, 1);

    tickCallback(map(delta, 0, 1, value.from, value.to));

    if (delta === 1) {
      cancelAnimationFrame(nextFrame);
    }
  };

  this.animate = fn => {
    if (typeof fn !== 'function') {
      throw new Error('The given callback is not a function. A valid function callback must be provided');
    }

    nextFrame = requestAnimationFrame(tick);
    tickCallback = fn;
  };

  return this;
}

Example of animating a number from -50 to 50 over 5000, starting slow and finishing slow:

const animation = new Animation({
  duration: 5000,
  from: -50,
  to: 50,
  easing: Easing.easeInOutQuad,
});

animation.animate(value => {
  document.body.innerText = String(Math.round(value));
});

I know there are many tiny animation libraries out there already, but I only need a small sub-set of their functionality.

Demo on JS Bin.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.