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\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.