David King

full stack developer

All Blog Articles

Prev « NexGen is Hiring! Next » Molecule Match, a Game


Easing Functions Refactored

11 Nov 2013

In the 2002 book, "Robert Penner's Programming Macromedia Flash MX" the author wrote a chapter about tweening; a word you may now know as "easing". Anyhoo, these easing functions have made their way into mainstream programming culture and have been ported over into every conceivable language and many popular frameworks.

Simply, easing functions are a way to animate in a smooth or interesting manner.

These functions have been all but unaltered for the past decade, however while looking into the relatively new gameclosure framework I found an opportunity to contribute by adding these functions to their javascript codebase.

So I did the logical thing, read some background information, found a modern port (jQuery) and started dissecting the code... what I found was bewildering and confusing.

Why do they accept 4 parameters?!

I might be missing the point, but these parameters make the functions difficult to read and complex to use outside of their intended animation scope. I wrongly surmised that the functions would accept one parameter akin to Math.sin, Math.cos or Math.tan, after which the transformations and manipulations could be applied.

So I modified the jQuery easing functions to accept one parameter:

n - A value between 0 and 1

If you consider the above parameters we can assume a special scenario:

Means that we can rewrite the functions to an extremely compact form, for example:

	// Original Function
	easeInQuad = function (t, b, c, d) {
		return c * (t /= d) * t + b;
	};
	// Swapping our special values
	easeInQuad = function (n) {
		return 1 * (n /= 1) * n + 0;
	};
	// Refactored
	easeInQuad = function (n) {
		return n * n;
	};

You can quickly see that my version is MUCH more readable. Furthermore, I've found an amazing oddity in the jQuery port of the elastic easing:

	// Original
	easeInElastic = function (t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	};
	// Expanded
	easeInElastic = function (t, b, c, d) {
		var s = 1.70158;
		var p = 0;
		var a = c;
		if (t == 0) {
			return b;
		}
		if ((t /= d) == 1) {
			return b + c;
		}
		if (!p) {
			p = d * .3;
		}
		if (a < Math.abs(c)) {
			a = c;
			var s = p / 4;
		} else {
			var s = p / (2 * Math.PI) * Math.asin(c / a);
		}
		return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
	};
	// Swapping our special values and adding comments...
	easeInElastic = function (n) {
		var s = 1.70158;
		var p = 0;
		var a = 1;
		if (n == 0) {
			return 0;
		}
		if ((n /= 1) == 1) {
			return 0 + 1;
		}
		// "p" is ALWAYS 0, why set it above?
		if (!p) {
			p = 1 * .3;
		}
		// "s" is ALWAYS replaced regardless, why set it above?
		if (a < Math.abs(1)) {
			// "a" is already set to 1... err...
			// not that it matters, as 1 is NEVER < 1 thus this is NEVER executed
			a = 1;
			var s = p / 4;
		} else {
			var s = p / (2 * Math.PI) * Math.asin(1 / a);
		}
		return -(a * Math.pow(2 , 10 * (n -= 1)) * Math.sin((n * 1 - s) * (2 * Math.PI) / p )) + 0;
	};
	// Refactoring A
	easeInElastic = function (n) {
		if (n == 0) {
			return 0;
		}
		if (n == 1) {
			return 1;
		}
		var p = 0.3;
		var s = p / (2 * Math.PI) * Math.asin(1); // This is ALWAYS 0.075
		return -(a * Math.pow(2 , 10 * (n -= 1)) * Math.sin((n * 1 - s) * (2 * Math.PI) / p )) + 0;
	};
	// Refactoring B
	easeInElastic = function (n) {
		if (n == 0) return 0;
		if (n == 1) return 1;
		var p = 0.3;
		var s = 0.075;	// p / (2 * Math.PI) * Math.asin(1)
		return -(Math.pow(2, 10 * (n -= 1)) * Math.sin((n - s) * (2 * Math.PI) / p));
	};

You may have missed it, but there's a LOT of redundant code and needless logical checks; the resulting function is not only faster, but in my mind just better.

Source Code

My full refactor is available on github here


Prev « NexGen is Hiring! Next » Molecule Match, a Game