Skip to content

Instantly share code, notes, and snippets.

@sole
Created October 19, 2010 10:11
Show Gist options
  • Save sole/633961 to your computer and use it in GitHub Desktop.
Save sole/633961 to your computer and use it in GitHub Desktop.
benchmarking tween.js easing functions
<script type="text/javascript">
TWEEN = {};
TWEEN.Easing = {};
TWEEN.ease = {};
TWEEN.Easing.LinearEaseNone = function ( k ) {
return k;
};
//
TWEEN.Easing.QuadraticEaseIn = function ( k ) {
return k * k;
};
TWEEN.Easing.QuadraticEaseOut = function ( k ) {
return - k * ( k - 2 );
};
TWEEN.Easing.QuadraticEaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k;
return - 0.5 * ( --k * ( k - 2 ) - 1 );
};
//
TWEEN.Easing.CubicEaseIn = function ( k ) {
return k * k * k;
};
TWEEN.Easing.CubicEaseOut = function ( k ) {
return --k * k * k + 1;
};
TWEEN.Easing.CubicEaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k;
return 0.5 * ( ( k -= 2 ) * k * k + 2 );
};
//
TWEEN.Easing.QuarticEaseIn = function ( k ) {
return k * k * k * k;
};
TWEEN.Easing.QuarticEaseOut = function ( k ) {
return - ( --k * k * k * k - 1 );
}
TWEEN.Easing.QuarticEaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1) return 0.5 * k * k * k * k;
return - 0.5 * ( ( k -= 2 ) * k * k * k - 2 );
};
//
TWEEN.Easing.QuinticEaseIn = function ( k ) {
return k * k * k * k * k;
};
TWEEN.Easing.QuinticEaseOut = function ( k ) {
return ( k = k - 1 ) * k * k * k * k + 1;
};
TWEEN.Easing.QuinticEaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k * k * k;
return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 );
};
//
TWEEN.Easing.SinusoidalEaseIn = function ( k ) {
return - Math.cos( k * Math.PI / 2 ) + 1;
};
TWEEN.Easing.SinusoidalEaseOut = function ( k ) {
return Math.sin( k * Math.PI / 2 );
};
TWEEN.Easing.SinusoidalEaseInOut = function ( k ) {
return - 0.5 * ( Math.cos( Math.PI * k ) - 1 );
};
//
TWEEN.Easing.ExponentialEaseIn = function ( k ) {
return k == 0 ? 0 : Math.pow( 2, 10 * ( k - 1 ) );
};
TWEEN.Easing.ExponentialEaseOut = function ( k ) {
return k == 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1;
};
TWEEN.Easing.ExponentialEaseInOut = function ( k ) {
if ( k == 0 ) return 0;
if ( k == 1 ) return 1;
if ( ( k *= 2 ) < 1 ) return 0.5 * Math.pow( 2, 10 * ( k - 1 ) );
return 0.5 * ( - Math.pow( 2, - 10 * ( k - 1 ) ) + 2 );
};
//
TWEEN.Easing.CircularEaseIn = function ( k ) {
return - ( Math.sqrt( 1 - k * k ) - 1);
};
TWEEN.Easing.CircularEaseOut = function ( k ) {
return Math.sqrt( 1 - --k * k );
};
TWEEN.Easing.CircularEaseInOut = function ( k ) {
if ( ( k /= 0.5 ) < 1) return - 0.5 * ( Math.sqrt( 1 - k * k) - 1);
return 0.5 * ( Math.sqrt( 1 - ( k -= 2) * k) + 1);
};
//
TWEEN.Easing.ElasticEaseIn = function( k ) {
var s, a = 0.1, p = 0.4;
if ( k == 0 ) return 0; if ( k == 1 ) return 1; if ( !p ) p = 0.3;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p / ( 2 * Math.PI ) * Math.asin( 1 / a );
return - ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) );
};
TWEEN.Easing.ElasticEaseOut = function( k ) {
var s, a = 0.1, p = 0.4;
if ( k == 0 ) return 0; if ( k == 1 ) return 1; if ( !p ) p = 0.3;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p / ( 2 * Math.PI ) * Math.asin( 1 / a );
return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 );
};
TWEEN.Easing.ElasticEaseInOut = function( k ) {
var s, a = 0.1, p = 0.4;
if ( k == 0 ) return 0; if ( k == 1 ) return 1; if ( !p ) p = 0.3;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p / ( 2 * Math.PI ) * Math.asin( 1 / a );
if ( ( k *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) );
return a * Math.pow( 2, -10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1;
};
//
TWEEN.Easing.BackEaseIn = function( k ) {
var s = 1.70158;
return k * k * ( ( s + 1 ) * k - s );
};
TWEEN.Easing.BackEaseOut = function( k ) {
var s = 1.70158;
return ( k = k - 1 ) * k * ( ( s + 1 ) * k + s ) + 1;
};
TWEEN.Easing.BackEaseInOut = function( k ) {
var s = 1.70158 * 1.525;
if ( ( k *= 2 ) < 1 ) return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) );
return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 );
};
//
TWEEN.Easing.BounceEaseIn = function( k ) {
return 1 - TWEEN.Easing.BounceEaseOut( 1 - k );
};
TWEEN.Easing.BounceEaseOut = function( k ) {
if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
return 7.5625 * k * k;
} else if ( k < ( 2 / 2.75 ) ) {
return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
} else if ( k < ( 2.5 / 2.75 ) ) {
return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
} else {
return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
}
};
TWEEN.Easing.BounceEaseInOut = function( k ) {
if ( k < 0.5 ) return TWEEN.Easing.BounceEaseIn( k * 2 ) * 0.5;
return TWEEN.Easing.BounceEaseOut( k * 2 - 1 ) * 0.5 + 0.5;
};
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
////////////////////////////
TWEEN.ease.LinearEaseNone = function(k) {
return k;
};
//
TWEEN.ease.QuadraticEaseIn = function(k) {
return k * k;
};
TWEEN.ease.QuadraticEaseOut = function(k) {
return -k * (k - 2);
};
TWEEN.ease.QuadraticEaseInOut = function(k) {
if ((k*= 2) < 1)
return 0.5 * k * k;
return -0.5 * (--k * (k - 2) - 1);
};
//
TWEEN.ease.CubicEaseIn = function(k) {
return k * k * k;
};
TWEEN.ease.CubicEaseOut = function(k) {
return 1 + --k * k * k;
};
TWEEN.ease.CubicEaseInOut = function(k) {
if ((k*= 2) < 1)
return 0.5 * k * k * k;
return 0.5 * ((k -= 2) * k * k + 2);
};
//
TWEEN.ease.QuarticEaseIn = function(k) {
return k * k * k * k;
};
TWEEN.ease.QuarticEaseOut = function(k) {
return -(--k * k * k * k - 1);
}
TWEEN.ease.QuarticEaseInOut = function(k) {
if ((k*= 2) < 1)
return 0.5 * k * k * k * k;
return - 0.5 * ((k-= 2) * k * k * k - 2);
};
//
TWEEN.ease.QuinticEaseIn = function(k) {
return k * k * k * k * k;
};
TWEEN.ease.QuinticEaseOut = function(k) {
return --k * k * k * k * k + 1;
};
TWEEN.ease.QuinticEaseInOut = function(k) {
if ((k*= 2) < 1)
return 0.5 * k * k * k * k * k;
return 0.5 * ((k-= 2) * k * k * k * k + 2);
};
//
TWEEN.ease.SinusoidalEaseIn = function(k) {
return 1 - Math.cos(k * Math.PI / 2);
};
TWEEN.ease.SinusoidalEaseOut = function(k) {
return Math.sin(k * Math.PI / 2);
};
TWEEN.ease.SinusoidalEaseInOut = function(k) {
return 0.5 - 0.5 * Math.cos(Math.PI * k);
};
//
TWEEN.ease.ExponentialEaseIn = function(k) {
return k !== 0 ? Math.pow(1024, k - 1) : 0;
};
TWEEN.ease.ExponentialEaseOut = function(k) {
return k !== 1 ? 1 - Math.pow(1024, -k) : 1;
};
TWEEN.ease.ExponentialEaseInOut = function(k) {
if (k === 0)
return 0;
if (k === 1)
return 1;
if ((k*= 2) < 1)
return 0.5 * Math.pow(1024, k - 1);
return 1 - 512 * Math.pow(1024, -k);
};
//
TWEEN.ease.CircularEaseIn = function(k) {
return 1 - Math.sqrt(1 - k * k);
};
TWEEN.ease.CircularEaseOut = function(k) {
return Math.sqrt(1 - --k * k);
};
TWEEN.ease.CircularEaseInOut = function(k) {
if ((k*= 2) < 1)
return 0.5 - 0.5 * Math.sqrt(1 - k * k);
return 0.5 + 0.5 * Math.sqrt(1 - (k-= 2) * k);
};
//
TWEEN.ease.ElasticEaseIn = function(k) {
if (k === 0)
return 0;
if (k === 1)
return 1;
return -(Math.pow(1024, --k) * Math.sin((k - 0.1) * 15.7079633));
};
TWEEN.ease.ElasticEaseOut = function(k) {
if (k === 0)
return 0;
if (k === 1)
return 1;
return 1 + Math.pow(1024, -k) * Math.sin((k - 0.1) * 15.7079633);
};
TWEEN.ease.ElasticEaseInOut = function(k) {
if (k === 0)
return 0;
if (k === 1)
return 1;
if ((k*= 2) < 1)
return -Math.pow(1024, --k) * 0.5 * Math.sin((k - 0.1) * 15.7079633);
return 1 + Math.pow(1024, - --k) * 0.5 * Math.sin((k - 0.1) * 15.7079633);
};
//
TWEEN.ease.BackEaseIn = function(k) {
return k * k * (2.70158 * k - 1.70158);
};
TWEEN.ease.BackEaseOut = function(k) {
return 1 + --k * k * (2.70158 * k + 1.70158);
};
TWEEN.ease.BackEaseInOut = function(k) {
var s = 1.70158 * 1.525;
if ((k*= 2) < 1)
return 0.5 * (k * k * ((s + 1) * k - s));
return 0.5 * ((k-= 2) * k * ((s + 1) * k + s) + 2);
};
//
TWEEN.ease.BounceEaseIn = function(k) {
return 1 - TWEEN.ease.BounceEaseOut(1 - k);
};
TWEEN.ease.BounceEaseOut = function(k) {
if (k < (4 / 11)) {
return 7.5625 * k * k;
} else if (k < (8 / 11)) {
return 0.75 + 7.5625 * (k-= 6 / 11) * k;
} else if (k < (10 / 11)) {
return 0.9375 + 7.5625 * (k-= 9 / 11) * k;
} else {
return 0.984375 + 7.5625 * (k-= 21 / 22) * k;
}
};
TWEEN.ease.BounceEaseInOut = function(k) {
if (k < 0.5)
return TWEEN.ease.BounceEaseIn(k * 2) * 0.5;
return TWEEN.ease.BounceEaseOut(k * 2 - 1) * 0.5 + 0.5;
};
(function(){
var j, start1, start2, end1, end2, timings = [];
var iterations = 1000000;
document.write('<table><tr><th>easing</th><th>new</th><th>old</th><th>%improvement</th></tr>');
for (var i in TWEEN.ease) {
start1 = new Date().getTime();
for(j=0; j<=iterations; j++) {
TWEEN.ease[i](j/iterations);
}
end1 = new Date().getTime();
start2 = new Date().getTime();
for(j=0; j<=iterations; j++) {
TWEEN.Easing[i](j/iterations);
}
end2 = new Date().getTime();
var t1 = end1 - start1;
var t2 = end2 - start2;
document.write('<tr><td>' + i + '</td><td>' + t1 + 'ms</td><td>' + t2 + 'ms</td><td>' + (t2 / t1 - 1) * 100 + '%</td></tr>');
}
document.write('</table>');
var newFunction, oldFunction, accumError;
document.write('<table><tr><th>function</th><th>AVG error</th></tr>');
for(var i in TWEEN.ease) {
newFunction = TWEEN.ease[i];
oldFunction = TWEEN.Easing[i];
accumError = 0;
for(j = 0; j < iterations; j++) {
var k = j / iterations;
var oldValue = oldFunction(k);
var newValue = newFunction(k);
var error = Math.abs(oldValue - newValue);
accumError += error;
}
document.write('<tr><td>' + i + '</td><td>' + (accumError / iterations) + '</td></tr>');
}
document.write('</table>');
})();
</script>
// JSLitmus.js
//
// Copyright (c) 2010, Robert Kieffer, http://broofa.com
// Available under MIT license (http://en.wikipedia.org/wiki/MIT_License)
(function() {
// Private methods and state
// Get platform info but don't go crazy trying to recognize everything
// that's out there. This is just for the major platforms and OSes.
var platform = 'unknown platform', ua = navigator.userAgent;
// Detect OS
var oses = ['Windows','iPhone OS','(Intel |PPC )?Mac OS X','Linux'].join('|');
var pOS = new RegExp('((' + oses + ') [^ \);]*)').test(ua) ? RegExp.$1 : null;
if (!pOS) pOS = new RegExp('((' + oses + ')[^ \);]*)').test(ua) ? RegExp.$1 : null;
// Detect browser
var pName = /(Chrome|MSIE|Safari|Opera|Firefox)/.test(ua) ? RegExp.$1 : null;
// Detect version
var vre = new RegExp('(Version|' + pName + ')[ \/]([^ ;]*)');
var pVersion = (pName && vre.test(ua)) ? RegExp.$2 : null;
var platform = (pOS && pName && pVersion) ? pName + ' ' + pVersion + ' on ' + pOS : 'unknown platform';
/**
* A smattering of methods that are needed to implement the JSLitmus testbed.
*/
var jsl = {
/**
* Enhanced version of escape()
*/
escape: function(s) {
s = s.replace(/,/g, '\\,');
s = escape(s);
s = s.replace(/\+/g, '%2b');
s = s.replace(/ /g, '+');
return s;
},
/**
* Get an element by ID.
*/
$: function(id) {
return document.getElementById(id);
},
/**
* Null function
*/
F: function() {},
/**
* Set the status shown in the UI
*/
status: function(msg) {
var el = jsl.$('jsl_status');
if (el) el.innerHTML = msg || '';
},
/**
* Convert a number to an abbreviated string like, "15K" or "10M"
*/
toLabel: function(n) {
if (n == Infinity) {
return 'Infinity';
} else if (n > 1e9) {
n = Math.round(n/1e8);
return n/10 + 'B';
} else if (n > 1e6) {
n = Math.round(n/1e5);
return n/10 + 'M';
} else if (n > 1e3) {
n = Math.round(n/1e2);
return n/10 + 'K';
}
return n;
},
/**
* Copy properties from src to dst
*/
extend: function(dst, src) {
for (var k in src) dst[k] = src[k]; return dst;
},
/**
* Like Array.join(), but for the key-value pairs in an object
*/
join: function(o, delimit1, delimit2) {
if (o.join) return o.join(delimit1); // If it's an array
var pairs = [];
for (var k in o) pairs.push(k + delimit1 + o[k]);
return pairs.join(delimit2);
},
/**
* Array#indexOf isn't supported in IE, so we use this as a cross-browser solution
*/
indexOf: function(arr, o) {
if (arr.indexOf) return arr.indexOf(o);
for (var i = 0; i < this.length; i++) if (arr[i] === o) return i;
return -1;
}
};
/**
* Test manages a single test (created with
* JSLitmus.test())
*
* @private
*/
var Test = function (name, f) {
if (!f) throw new Error('Undefined test function');
if (!/function[^\(]*\(([^,\)]*)/.test(f.toString())) {
throw new Error('"' + name + '" test: Test is not a valid Function object');
}
this.loopArg = RegExp.$1;
this.name = name;
this.f = f;
};
jsl.extend(Test, /** @lends Test */ {
/** Calibration tests for establishing iteration loop overhead */
CALIBRATIONS: [
new Test('calibrating loop', function(count) {while (count--);}),
new Test('calibrating function', jsl.F)
],
/**
* Run calibration tests. Returns true if calibrations are not yet
* complete (in which case calling code should run the tests yet again).
* onCalibrated - Callback to invoke when calibrations have finished
*/
calibrate: function(onCalibrated) {
for (var i = 0; i < Test.CALIBRATIONS.length; i++) {
var cal = Test.CALIBRATIONS[i];
if (cal.running) return true;
if (!cal.count) {
cal.isCalibration = true;
cal.onStop = onCalibrated;
//cal.MIN_TIME = .1; // Do calibrations quickly
cal.run(2e4);
return true;
}
}
return false;
}
});
jsl.extend(Test.prototype, {/** @lends Test.prototype */
/** Initial number of iterations */
INIT_COUNT: 10,
/** Max iterations allowed (i.e. used to detect bad looping functions) */
MAX_COUNT: 1e9,
/** Minimum time a test should take to get valid results (secs) */
MIN_TIME: .5,
/** Callback invoked when test state changes */
onChange: jsl.F,
/** Callback invoked when test is finished */
onStop: jsl.F,
/**
* Reset test state
*/
reset: function() {
delete this.count;
delete this.time;
delete this.running;
delete this.error;
},
/**
* Run the test (in a timeout). We use a timeout to make sure the browser
* has a chance to finish rendering any UI changes we've made, like
* updating the status message.
*/
run: function(count) {
count = count || this.INIT_COUNT;
jsl.status(this.name + ' x ' + count);
this.running = true;
var me = this;
setTimeout(function() {me._run(count);}, 200);
},
/**
* The nuts and bolts code that actually runs a test
*/
_run: function(count) {
var me = this;
// Make sure calibration tests have run
if (!me.isCalibration && Test.calibrate(function() {me.run(count);})) return;
this.error = null;
try {
var start, f = this.f, now, i = count;
// Start the timer
start = new Date();
// Now for the money shot. If this is a looping function ...
if (this.loopArg) {
// ... let it do the iteration itself
f(count);
} else {
// ... otherwise do the iteration for it
while (i--) f();
}
// Get time test took (in secs)
this.time = Math.max(1,new Date() - start)/1000;
// Store iteration count and per-operation time taken
this.count = count;
this.period = this.time/count;
// Do we need to do another run?
this.running = this.time <= this.MIN_TIME;
// ... if so, compute how many times we should iterate
if (this.running) {
// Bump the count to the nearest power of 2
var x = this.MIN_TIME/this.time;
var pow = Math.pow(2, Math.max(1, Math.ceil(Math.log(x)/Math.log(2))));
count *= pow;
if (count > this.MAX_COUNT) {
throw new Error('Max count exceeded. If this test uses a looping function, make sure the iteration loop is working properly.');
}
}
} catch (e) {
// Exceptions are caught and displayed in the test UI
this.reset();
this.error = e;
}
// Figure out what to do next
if (this.running) {
me.run(count);
} else {
jsl.status('');
me.onStop(me);
}
// Finish up
this.onChange(this);
},
/**
* Get the number of operations per second for this test.
*
* @param normalize if true, iteration loop overhead taken into account
*/
getHz: function(/**Boolean*/ normalize) {
var p = this.period;
// Adjust period based on the calibration test time
if (normalize && !this.isCalibration) {
var cal = Test.CALIBRATIONS[this.loopArg ? 0 : 1];
// If the period is within 20% of the calibration time, then zero the
// it out
p = p < cal.period*1.2 ? 0 : p - cal.period;
}
return Math.round(1/p);
},
/**
* Get a friendly string describing the test
*/
toString: function() {
return this.name + ' - ' + this.time/this.count + ' secs';
}
});
// CSS we need for the UI
var STYLESHEET = '<style> \
#jslitmus {font-family:sans-serif; font-size: 12px;} \
#jslitmus a {text-decoration: none;} \
#jslitmus a:hover {text-decoration: underline;} \
#jsl_status { \
margin-top: 10px; \
font-size: 10px; \
color: #888; \
} \
A IMG {border:none} \
#test_results { \
margin-top: 10px; \
font-size: 12px; \
font-family: sans-serif; \
border-collapse: collapse; \
border-spacing: 0px; \
} \
#test_results th, #test_results td { \
border: solid 1px #ccc; \
vertical-align: top; \
padding: 3px; \
} \
#test_results th { \
vertical-align: bottom; \
background-color: #ccc; \
padding: 1px; \
font-size: 10px; \
} \
#test_results #test_platform { \
color: #444; \
text-align:center; \
} \
#test_results .test_row { \
color: #006; \
cursor: pointer; \
} \
#test_results .test_nonlooping { \
border-left-style: dotted; \
border-left-width: 2px; \
} \
#test_results .test_looping { \
border-left-style: solid; \
border-left-width: 2px; \
} \
#test_results .test_name {white-space: nowrap;} \
#test_results .test_pending { \
} \
#test_results .test_running { \
font-style: italic; \
} \
#test_results .test_done {} \
#test_results .test_done { \
text-align: right; \
font-family: monospace; \
} \
#test_results .test_error {color: #600;} \
#test_results .test_error .error_head {font-weight:bold;} \
#test_results .test_error .error_body {font-size:85%;} \
#test_results .test_row:hover td { \
background-color: #ffc; \
text-decoration: underline; \
} \
#chart { \
margin: 10px 0px; \
width: 250px; \
} \
#chart img { \
border: solid 1px #ccc; \
margin-bottom: 5px; \
} \
#chart #tiny_url { \
height: 40px; \
width: 250px; \
} \
#jslitmus_credit { \
font-size: 10px; \
color: #888; \
margin-top: 8px; \
} \
</style>';
// HTML markup for the UI
var MARKUP = '<div id="jslitmus"> \
<button onclick="JSLitmus.runAll(event)">Run Tests</button> \
<button id="stop_button" disabled="disabled" onclick="JSLitmus.stop()">Stop Tests</button> \
<br \> \
<br \> \
<input type="checkbox" style="vertical-align: middle" id="test_normalize" checked="checked" onchange="JSLitmus.renderAll()""> Normalize results \
<table id="test_results"> \
<colgroup> \
<col /> \
<col width="100" /> \
</colgroup> \
<tr><th id="test_platform" colspan="2">' + platform + '</th></tr> \
<tr><th>Test</th><th>Ops/sec</th></tr> \
<tr id="test_row_template" class="test_row" style="display:none"> \
<td class="test_name"></td> \
<td class="test_result">Ready</td> \
</tr> \
</table> \
<div id="jsl_status"></div> \
<div id="chart" style="display:none"> \
<a id="chart_link" target="_blank"><img id="chart_image"></a> \
TinyURL (for chart): \
<iframe id="tiny_url" frameBorder="0" scrolling="no" src=""></iframe> \
</div> \
<a id="jslitmus_credit" title="JSLitmus home page" href="http://code.google.com/p/jslitmus" target="_blank">Powered by JSLitmus</a> \
</div>';
/**
* The public API for creating and running tests
*/
window.JSLitmus = {
/** The list of all tests that have been registered with JSLitmus.test */
_tests: [],
/** The queue of tests that need to be run */
_queue: [],
/**
* The parsed query parameters the current page URL. This is provided as a
* convenience for test functions - it's not used by JSLitmus proper
*/
params: {},
/**
* Initialize
*/
_init: function() {
// Parse query params into JSLitmus.params[] hash
var match = (location + '').match(/([^?#]*)(#.*)?$/);
if (match) {
var pairs = match[1].split('&');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].split('=');
if (pair.length > 1) {
var key = pair.shift();
var value = pair.length > 1 ? pair.join('=') : pair[0];
this.params[key] = value;
}
}
}
// Write out the stylesheet. We have to do this here because IE
// doesn't honor sheets written after the document has loaded.
document.write(STYLESHEET);
// Setup the rest of the UI once the document is loaded
if (window.addEventListener) {
window.addEventListener('load', this._setup, false);
} else if (document.addEventListener) {
document.addEventListener('load', this._setup, false);
} else if (window.attachEvent) {
window.attachEvent('onload', this._setup);
}
return this;
},
/**
* Set up the UI
*/
_setup: function() {
var el = jsl.$('jslitmus_container');
if (!el) document.body.appendChild(el = document.createElement('div'));
el.innerHTML = MARKUP;
// Render the UI for all our tests
for (var i=0; i < JSLitmus._tests.length; i++)
JSLitmus.renderTest(JSLitmus._tests[i]);
},
/**
* (Re)render all the test results
*/
renderAll: function() {
for (var i = 0; i < JSLitmus._tests.length; i++)
JSLitmus.renderTest(JSLitmus._tests[i]);
JSLitmus.renderChart();
},
/**
* (Re)render the chart graphics
*/
renderChart: function() {
var url = JSLitmus.chartUrl();
jsl.$('chart_link').href = url;
jsl.$('chart_image').src = url;
jsl.$('chart').style.display = '';
// Update the tiny URL
jsl.$('tiny_url').src = 'http://tinyurl.com/api-create.php?url='+escape(url);
},
/**
* (Re)render the results for a specific test
*/
renderTest: function(test) {
// Make a new row if needed
if (!test._row) {
var trow = jsl.$('test_row_template');
if (!trow) return;
test._row = trow.cloneNode(true);
test._row.style.display = '';
test._row.id = '';
test._row.onclick = function() {JSLitmus._queueTest(test);};
test._row.title = 'Run ' + test.name + ' test';
trow.parentNode.appendChild(test._row);
test._row.cells[0].innerHTML = test.name;
}
var cell = test._row.cells[1];
var cns = [test.loopArg ? 'test_looping' : 'test_nonlooping'];
if (test.error) {
cns.push('test_error');
cell.innerHTML =
'<div class="error_head">' + test.error + '</div>' +
'<ul class="error_body"><li>' +
jsl.join(test.error, ': ', '</li><li>') +
'</li></ul>';
} else {
if (test.running) {
cns.push('test_running');
cell.innerHTML = 'running';
} else if (jsl.indexOf(JSLitmus._queue, test) >= 0) {
cns.push('test_pending');
cell.innerHTML = 'pending';
} else if (test.count) {
cns.push('test_done');
var hz = test.getHz(jsl.$('test_normalize').checked);
cell.innerHTML = hz != Infinity ? hz : '&infin;';
cell.title = 'Looped ' + test.count + ' times in ' + test.time + ' seconds';
} else {
cell.innerHTML = 'ready';
}
}
cell.className = cns.join(' ');
},
/**
* Create a new test
*/
test: function(name, f) {
// Create the Test object
var test = new Test(name, f);
JSLitmus._tests.push(test);
// Re-render if the test state changes
test.onChange = JSLitmus.renderTest;
// Run the next test if this one finished
test.onStop = function(test) {
if (JSLitmus.onTestFinish) JSLitmus.onTestFinish(test);
JSLitmus.currentTest = null;
JSLitmus._nextTest();
};
// Render the new test
this.renderTest(test);
},
/**
* Add all tests to the run queue
*/
runAll: function(e) {
e = e || window.event;
var reverse = e && e.shiftKey, len = JSLitmus._tests.length;
for (var i = 0; i < len; i++) {
JSLitmus._queueTest(JSLitmus._tests[!reverse ? i : (len - i - 1)]);
}
},
/**
* Remove all tests from the run queue. The current test has to finish on
* it's own though
*/
stop: function() {
while (JSLitmus._queue.length) {
var test = JSLitmus._queue.shift();
JSLitmus.renderTest(test);
}
},
/**
* Run the next test in the run queue
*/
_nextTest: function() {
if (!JSLitmus.currentTest) {
var test = JSLitmus._queue.shift();
if (test) {
jsl.$('stop_button').disabled = false;
JSLitmus.currentTest = test;
test.run();
JSLitmus.renderTest(test);
if (JSLitmus.onTestStart) JSLitmus.onTestStart(test);
} else {
jsl.$('stop_button').disabled = true;
JSLitmus.renderChart();
}
}
},
/**
* Add a test to the run queue
*/
_queueTest: function(test) {
if (jsl.indexOf(JSLitmus._queue, test) >= 0) return;
JSLitmus._queue.push(test);
JSLitmus.renderTest(test);
JSLitmus._nextTest();
},
/**
* Generate a Google Chart URL that shows the data for all tests
*/
chartUrl: function() {
var n = JSLitmus._tests.length, markers = [], data = [];
var d, min = 0, max = -1e10;
var normalize = jsl.$('test_normalize').checked;
// Gather test data
for (var i=0; i < JSLitmus._tests.length; i++) {
var test = JSLitmus._tests[i];
if (test.count) {
var hz = test.getHz(normalize);
var v = hz != Infinity ? hz : 0;
data.push(v);
markers.push('t' + jsl.escape(test.name + '(' + jsl.toLabel(hz)+ ')') + ',000000,0,' +
markers.length + ',10');
max = Math.max(v, max);
}
}
if (markers.length <= 0) return null;
// Build chart title
var title = document.getElementsByTagName('title');
title = (title && title.length) ? title[0].innerHTML : null;
var chart_title = [];
if (title) chart_title.push(title);
chart_title.push('Ops/sec (' + platform + ')');
// Build labels
var labels = [jsl.toLabel(min), jsl.toLabel(max)];
var w = 250, bw = 15;
var bs = 5;
var h = markers.length*(bw + bs) + 30 + chart_title.length*20;
var params = {
chtt: escape(chart_title.join('|')),
chts: '000000,10',
cht: 'bhg', // chart type
chd: 't:' + data.join(','), // data set
chds: min + ',' + max, // max/min of data
chxt: 'x', // label axes
chxl: '0:|' + labels.join('|'), // labels
chsp: '0,1',
chm: markers.join('|'), // test names
chbh: [bw, 0, bs].join(','), // bar widths
// chf: 'bg,lg,0,eeeeee,0,eeeeee,.5,ffffff,1', // gradient
chs: w + 'x' + h
};
return 'http://chart.apis.google.com/chart?' + jsl.join(params, '=', '&');
}
};
JSLitmus._init();
})();
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>tween.js benchmarking</title>
<script type="text/javascript" src="JSLitmus.js"></script>
<script type="text/javascript">
TWEEN = {};
TWEEN.Easing = { Linear: {}, Quadratic: {}, Cubic: {}, Quartic: {}, Quintic: {}, Sinusoidal: {}, Exponential: {}, Circular: {}, Elastic: {}, Back: {}, Bounce: {} };
TWEEN.Easing.Linear.EaseNone = function ( k ) {
return k;
};
//
TWEEN.Easing.Quadratic.EaseIn = function ( k ) {
return k * k;
};
TWEEN.Easing.Quadratic.EaseOut = function ( k ) {
return - k * ( k - 2 );
};
TWEEN.Easing.Quadratic.EaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k;
return - 0.5 * ( --k * ( k - 2 ) - 1 );
};
//
TWEEN.Easing.Cubic.EaseIn = function ( k ) {
return k * k * k;
};
TWEEN.Easing.Cubic.EaseOut = function ( k ) {
return --k * k * k + 1;
};
TWEEN.Easing.Cubic.EaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k;
return 0.5 * ( ( k -= 2 ) * k * k + 2 );
};
//
TWEEN.Easing.Quartic.EaseIn = function ( k ) {
return k * k * k * k;
};
TWEEN.Easing.Quartic.EaseOut = function ( k ) {
return - ( --k * k * k * k - 1 );
}
TWEEN.Easing.Quartic.EaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1) return 0.5 * k * k * k * k;
return - 0.5 * ( ( k -= 2 ) * k * k * k - 2 );
};
//
TWEEN.Easing.Quintic.EaseIn = function ( k ) {
return k * k * k * k * k;
};
TWEEN.Easing.Quintic.EaseOut = function ( k ) {
return ( k = k - 1 ) * k * k * k * k + 1;
};
TWEEN.Easing.Quintic.EaseInOut = function ( k ) {
if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k * k * k;
return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 );
};
//
TWEEN.Easing.Sinusoidal.EaseIn = function ( k ) {
return - Math.cos( k * Math.PI / 2 ) + 1;
};
TWEEN.Easing.Sinusoidal.EaseOut = function ( k ) {
return Math.sin( k * Math.PI / 2 );
};
TWEEN.Easing.Sinusoidal.EaseInOut = function ( k ) {
return - 0.5 * ( Math.cos( Math.PI * k ) - 1 );
};
//
TWEEN.Easing.Exponential.EaseIn = function ( k ) {
return k == 0 ? 0 : Math.pow( 2, 10 * ( k - 1 ) );
};
TWEEN.Easing.Exponential.EaseOut = function ( k ) {
return k == 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1;
};
TWEEN.Easing.Exponential.EaseInOut = function ( k ) {
if ( k == 0 ) return 0;
if ( k == 1 ) return 1;
if ( ( k *= 2 ) < 1 ) return 0.5 * Math.pow( 2, 10 * ( k - 1 ) );
return 0.5 * ( - Math.pow( 2, - 10 * ( k - 1 ) ) + 2 );
};
//
TWEEN.Easing.Circular.EaseIn = function ( k ) {
return - ( Math.sqrt( 1 - k * k ) - 1);
};
TWEEN.Easing.Circular.EaseOut = function ( k ) {
return Math.sqrt( 1 - --k * k );
};
TWEEN.Easing.Circular.EaseInOut = function ( k ) {
if ( ( k /= 0.5 ) < 1) return - 0.5 * ( Math.sqrt( 1 - k * k) - 1);
return 0.5 * ( Math.sqrt( 1 - ( k -= 2) * k) + 1);
};
//
TWEEN.Easing.Elastic.EaseIn = function( k ) {
var s, a = 0.1, p = 0.4;
if ( k == 0 ) return 0; if ( k == 1 ) return 1; if ( !p ) p = 0.3;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p / ( 2 * Math.PI ) * Math.asin( 1 / a );
return - ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) );
};
TWEEN.Easing.Elastic.EaseOut = function( k ) {
var s, a = 0.1, p = 0.4;
if ( k == 0 ) return 0; if ( k == 1 ) return 1; if ( !p ) p = 0.3;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p / ( 2 * Math.PI ) * Math.asin( 1 / a );
return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 );
};
TWEEN.Easing.Elastic.EaseInOut = function( k ) {
var s, a = 0.1, p = 0.4;
if ( k == 0 ) return 0; if ( k == 1 ) return 1; if ( !p ) p = 0.3;
if ( !a || a < 1 ) { a = 1; s = p / 4; }
else s = p / ( 2 * Math.PI ) * Math.asin( 1 / a );
if ( ( k *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) );
return a * Math.pow( 2, -10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1;
};
//
TWEEN.Easing.Back.EaseIn = function( k ) {
var s = 1.70158;
return k * k * ( ( s + 1 ) * k - s );
};
TWEEN.Easing.Back.EaseOut = function( k ) {
var s = 1.70158;
return ( k = k - 1 ) * k * ( ( s + 1 ) * k + s ) + 1;
};
TWEEN.Easing.Back.EaseInOut = function( k ) {
var s = 1.70158 * 1.525;
if ( ( k *= 2 ) < 1 ) return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) );
return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 );
};
//
TWEEN.Easing.Bounce.EaseIn = function( k ) {
return 1 - TWEEN.Easing.Bounce.EaseOut( 1 - k );
};
TWEEN.Easing.Bounce.EaseOut = function( k ) {
if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
return 7.5625 * k * k;
} else if ( k < ( 2 / 2.75 ) ) {
return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
} else if ( k < ( 2.5 / 2.75 ) ) {
return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
} else {
return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
}
};
TWEEN.Easing.Bounce.EaseInOut = function( k ) {
if ( k < 0.5 ) return TWEEN.Easing.Bounce.EaseIn( k * 2 ) * 0.5;
return TWEEN.Easing.Bounce.EaseOut( k * 2 - 1 ) * 0.5 + 0.5;
};
////////////////////////////
////////////////////////////
TWEEN.ease = {};
TWEEN.ease.LinearEaseNone = function(k) {
return k;
};
//
TWEEN.ease.QuadraticEaseIn = function(k) {
return k * k;
};
TWEEN.ease.QuadraticEaseOut = function(k) {
return -k * (k - 2);
};
TWEEN.ease.QuadraticEaseInOut = function(k) {
if ((k*= 2) < 1)
return 0.5 * k * k;
return -0.5 * (--k * (k - 2) - 1);
};
//
TWEEN.ease.CubicEaseIn = function(k) {
return k * k * k;
};
TWEEN.ease.CubicEaseOut = function(k) {
return 1 + --k * k * k;
};
TWEEN.ease.CubicEaseInOut = function(k) {
if ((k*= 2) < 1)
return 0.5 * k * k * k;
return 0.5 * ((k -= 2) * k * k + 2);
};
//
TWEEN.ease.QuarticEaseIn = function(k) {
return k * k * k * k;
};
TWEEN.ease.QuarticEaseOut = function(k) {
return -(--k * k * k * k - 1);
}
TWEEN.ease.QuarticEaseInOut = function(k) {
if ((k*= 2) < 1)
return 0.5 * k * k * k * k;
return - 0.5 * ((k-= 2) * k * k * k - 2);
};
//
TWEEN.ease.QuinticEaseIn = function(k) {
return k * k * k * k * k;
};
TWEEN.ease.QuinticEaseOut = function(k) {
return --k * k * k * k * k + 1;
};
TWEEN.ease.QuinticEaseInOut = function(k) {
if ((k*= 2) < 1)
return 0.5 * k * k * k * k * k;
return 0.5 * ((k-= 2) * k * k * k * k + 2);
};
//
TWEEN.ease.SinusoidalEaseIn = function(k) {
return 1 - Math.cos(k * Math.PI / 2);
};
TWEEN.ease.SinusoidalEaseOut = function(k) {
return Math.sin(k * Math.PI / 2);
};
TWEEN.ease.SinusoidalEaseInOut = function(k) {
return 0.5 - 0.5 * Math.cos(Math.PI * k);
};
//
TWEEN.ease.ExponentialEaseIn = function(k) {
return k !== 0 ? Math.pow(1024, k - 1) : 0;
};
TWEEN.ease.ExponentialEaseOut = function(k) {
return k !== 1 ? 1 - Math.pow(1024, -k) : 1;
};
TWEEN.ease.ExponentialEaseInOut = function(k) {
if (k === 0)
return 0;
if (k === 1)
return 1;
if ((k*= 2) < 1)
return 0.5 * Math.pow(1024, k - 1);
return 1 - 512 * Math.pow(1024, -k);
};
//
TWEEN.ease.CircularEaseIn = function(k) {
return 1 - Math.sqrt(1 - k * k);
};
TWEEN.ease.CircularEaseOut = function(k) {
return Math.sqrt(1 - --k * k);
};
TWEEN.ease.CircularEaseInOut = function(k) {
if ((k*= 2) < 1)
return 0.5 - 0.5 * Math.sqrt(1 - k * k);
return 0.5 + 0.5 * Math.sqrt(1 - (k-= 2) * k);
};
//
TWEEN.ease.ElasticEaseIn = function(k) {
if (k === 0)
return 0;
if (k === 1)
return 1;
return -(Math.pow(1024, --k) * Math.sin((k - 0.1) * 15.7079633));
};
TWEEN.ease.ElasticEaseOut = function(k) {
if (k === 0)
return 0;
if (k === 1)
return 1;
return 1 + Math.pow(1024, -k) * Math.sin((k - 0.1) * 15.7079633);
};
TWEEN.ease.ElasticEaseInOut = function(k) {
if (k === 0)
return 0;
if (k === 1)
return 1;
if ((k*= 2) < 1)
return -Math.pow(1024, --k) * 0.5 * Math.sin((k - 0.1) * 15.7079633);
return 1 + Math.pow(1024, - --k) * 0.5 * Math.sin((k - 0.1) * 15.7079633);
};
//
TWEEN.ease.BackEaseIn = function(k) {
return k * k * (2.70158 * k - 1.70158);
};
TWEEN.ease.BackEaseOut = function(k) {
return 1 + --k * k * (2.70158 * k + 1.70158);
};
TWEEN.ease.BackEaseInOut = function(k) {
var s = 1.70158 * 1.525;
if ((k*= 2) < 1)
return 0.5 * (k * k * ((s + 1) * k - s));
return 0.5 * ((k-= 2) * k * ((s + 1) * k + s) + 2);
};
//
TWEEN.ease.BounceEaseIn = function(k) {
return 1 - TWEEN.ease.BounceEaseOut(1 - k);
};
TWEEN.ease.BounceEaseOut = function(k) {
if (k < (4 / 11)) {
return 7.5625 * k * k;
} else if (k < (8 / 11)) {
return 0.75 + 7.5625 * (k-= 6 / 11) * k;
} else if (k < (10 / 11)) {
return 0.9375 + 7.5625 * (k-= 9 / 11) * k;
} else {
return 0.984375 + 7.5625 * (k-= 21 / 22) * k;
}
};
TWEEN.ease.BounceEaseInOut = function(k) {
if (k < 0.5)
return TWEEN.ease.BounceEaseIn(k * 2) * 0.5;
return TWEEN.ease.BounceEaseOut(k * 2 - 1) * 0.5 + 0.5;
};
// -------------
var benchmarkFunctions = [
['Quadratic/EaseInOut', TWEEN.Easing.Quadratic.EaseInOut, TWEEN.ease.QuadraticEaseInOut],
['Cubic/EaseIn', TWEEN.Easing.Cubic.EaseIn, TWEEN.ease.CubicEaseIn],
['Cubic/EaseOut', TWEEN.Easing.Cubic.EaseOut, TWEEN.ease.CubicEaseOut],
['Cubic/EaseInOut', TWEEN.Easing.Cubic.EaseInOut, TWEEN.ease.CubicEaseInOut],
['Quartic/EaseIn', TWEEN.Easing.Quartic.EaseIn, TWEEN.ease.QuarticEaseIn],
['Quartic/EaseOut', TWEEN.Easing.Quartic.EaseOut, TWEEN.ease.QuarticEaseOut],
['Quartic/EaseInOut', TWEEN.Easing.Quartic.EaseInOut, TWEEN.ease.QuarticEaseInOut],
['Quintic/EaseIn', TWEEN.Easing.Quintic.EaseIn, TWEEN.ease.QuinticEaseIn],
['Quintic/EaseOut', TWEEN.Easing.Quintic.EaseOut, TWEEN.ease.QuinticEaseOut],
['Quintic/EaseInOut', TWEEN.Easing.Quintic.EaseInOut, TWEEN.ease.QuinticEaseInOut],
['Sinusoidal/EaseIn', TWEEN.Easing.Sinusoidal.EaseIn, TWEEN.ease.SinusoidalEaseIn],
['Sinusoidal/EaseOut', TWEEN.Easing.Sinusoidal.EaseOut, TWEEN.ease.SinusoidalEaseOut],
['Sinusoidal/EaseInOut', TWEEN.Easing.Sinusoidal.EaseInOut, TWEEN.ease.SinusoidalEaseInOut],
['Exponential/EaseIn', TWEEN.Easing.Exponential.EaseIn, TWEEN.ease.ExponentialEaseIn],
['Exponential/EaseOut', TWEEN.Easing.Exponential.EaseOut, TWEEN.ease.ExponentialEaseOut],
['Exponential/EaseInOut', TWEEN.Easing.Exponential.EaseInOut, TWEEN.ease.ExponentialEaseInOut],
['Circular/EaseIn', TWEEN.Easing.Circular.EaseIn, TWEEN.ease.CircularEaseIn],
['Circular/EaseOut', TWEEN.Easing.Circular.EaseOut, TWEEN.ease.CircularEaseOut],
['Circular/EaseInOut', TWEEN.Easing.Circular.EaseInOut, TWEEN.ease.CircularEaseInOut],
['Elastic/EaseIn', TWEEN.Easing.Elastic.EaseIn, TWEEN.ease.ElasticEaseIn],
['Elastic/EaseOut', TWEEN.Easing.Elastic.EaseOut, TWEEN.ease.ElasticEaseOut],
['Elastic/EaseInOut', TWEEN.Easing.Elastic.EaseInOut, TWEEN.ease.ElasticEaseInOut],
['Back/EaseIn', TWEEN.Easing.Back.EaseIn, TWEEN.ease.BackEaseIn],
['Back/EaseOut', TWEEN.Easing.Back.EaseOut, TWEEN.ease.BackEaseOut],
['Back/EaseInOut', TWEEN.Easing.Back.EaseInOut, TWEEN.ease.BackEaseInOut],
['Bounce/EaseIn', TWEEN.Easing.Bounce.EaseIn, TWEEN.ease.BounceEaseIn],
['Bounce/EaseOut', TWEEN.Easing.Bounce.EaseOut, TWEEN.ease.BounceEaseOut],
['Bounce/EaseInOut', TWEEN.Easing.Bounce.EaseInOut, TWEEN.ease.BounceEaseInOut],
];
function makeTestFunction(f) {
var testFunction = function(count) {
var start = count;
while(count--) {
f(count / start);
}
}
return testFunction;
}
for(var i = 0; i < benchmarkFunctions.length; i++) {
var func = benchmarkFunctions[i];
var title = func[0];
var original = func[1];
var optimised = func[2];
JSLitmus.test(title, makeTestFunction(original));
JSLitmus.test(title + ' * OPT', makeTestFunction(optimised));
}
</script>
</head>
<body>
<div id="test_element" style="overflow:hidden; width: 1px; height:1px;"></div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment