<!-- MovingBoxes.html Tests performance of transforms vs. top/left Created by Jonathan Deutsch <jonathan@tumult.com> Copyright (c) 2013 Tumult Inc. --> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <style> #container { width: 800px; height: 600px; position: relative; } </style> <script> // from http://paulirish.com/2011/requestanimationframe-for-smart-animating/ window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); }; })(); // modified from source found at: http://frugalcoder.us/post/2010/09/13/browser-detection.aspx var kBrowserInfo = (function () { var b = {}; if (!navigator) return b; //browsermatch method... function bm(re) { var m = (navigator && navigator.userAgent && navigator.userAgent.match(re)); return (m && m[1]); } //setup browser detection b.ie = parseFloat(bm(/MSIE (\d+\.\d+)/)) || null; b.gecko = parseFloat(bm(/Gecko\/(\d+)/)) || null; b.ff = parseFloat(bm(/Firefox\/(\d+\.\d+)/)) || null; b.khtml = parseFloat(bm(/\((KHTML)/) && 1) || null; b.webkit = parseFloat(bm(/AppleWebKit\/(\d+\.\d+)/)); b.chrome = parseFloat(b.webkit && bm(/Chrome\/(\d+\.\d+)/)) || null; b.safari = parseFloat(b.webkit && bm(/Safari\/(\d+\.\d+)/) && bm(/Version\/(\d+\.\d+)/)) || null; b.opera = parseFloat(bm(/Opera\/(\d+\.\d+)/) && bm(/Version\/(\d+\.\d+)/)) || bm(/Opera\/(\d+\.\d+)/) || null; b.android = (navigator.userAgent.search("Android") > -1) || null; b.ipad = (navigator.userAgent.search("iPad") > -1) || null; b.iphone = (navigator.userAgent.search("iPhone") > -1) || null; b.ipod = (navigator.userAgent.search("iPod") > -1) || null; b.ios = b.ipad || b.iphone || b.ipod || null; //delete empty values for (var x in b) { if (b[x] === null || isNaN(b[x])) delete b[x]; } //disable IE matching for older Opera versions. if (b.opera && b.ie) delete b.ie; return b; }()); var kTotalBoxCount = 2000; var kBoxWidth = 100; var kBoxHeight = 100; var kContainerWidth = 800; var kContainerHeight = 600; var kTestDuration = 10.0; var kMaxTestIterations = 1; var kTransformName = "transform"; var kTransitionName = "transition"; if(kBrowserInfo.webkit != null) { kTransformName = "-webkit-transform"; kTransitionName = "-webkit-transition"; } var gBoxes = Array(); var gStartTime = null; var gFrameCount = 0; var gTestMode = "transition"; // "javascript" var gTestProperty = "topleft"; // "topleft" var gShouldForce3D = false; // false var gOpaque = true; var gCurrentTestIndex = 0; var gCurrentTestIteration = 0; // test matrix // transition style rotateY opacity // var kTestsMatrix = [ { "mode" : "javascript", "property" : "topleft", "shouldForce3D" : true, "opaque" : true }, { "mode" : "javascript", "property" : "topleft", "shouldForce3D" : false, "opaque" : true }, { "mode" : "javascript", "property" : "translate", "shouldForce3D" : true, "opaque" : true }, { "mode" : "javascript", "property" : "translate", "shouldForce3D" : false, "opaque" : true }, { "mode" : "transition", "property" : "topleft", "shouldForce3D" : true, "opaque" : true }, { "mode" : "transition", "property" : "topleft", "shouldForce3D" : false, "opaque" : true }, { "mode" : "transition", "property" : "translate", "shouldForce3D" : false, "opaque" : true }, { "mode" : "transition", "property" : "translate", "shouldForce3D" : true, "opaque" : true }, { "mode" : "javascript", "property" : "topleft", "shouldForce3D" : true, "opaque" : false }, { "mode" : "javascript", "property" : "topleft", "shouldForce3D" : false, "opaque" : false }, { "mode" : "javascript", "property" : "translate", "shouldForce3D" : true, "opaque" : false }, { "mode" : "javascript", "property" : "translate", "shouldForce3D" : false, "opaque" : false }, { "mode" : "transition", "property" : "topleft", "shouldForce3D" : true, "opaque" : false }, { "mode" : "transition", "property" : "topleft", "shouldForce3D" : false, "opaque" : false }, { "mode" : "transition", "property" : "translate", "shouldForce3D" : false, "opaque" : false }, { "mode" : "transition", "property" : "translate", "shouldForce3D" : true, "opaque" : false }, ]; function beginTesting() { runNextTest(); } function runNextTest() { var testInfo = kTestsMatrix[gCurrentTestIndex]; if(gCurrentTestIndex < kTestsMatrix.length) { document.getElementById("output").innerHTML += "" + gCurrentTestIndex + " "; runTest(testInfo.mode, testInfo.property, testInfo.shouldForce3D, testInfo.opaque); } if(gCurrentTestIteration + 1 < kMaxTestIterations) { gCurrentTestIteration += 1; } else { gCurrentTestIteration = 0; gCurrentTestIndex += 1; } } function runTest(mode, property, shouldForce3D, opaque) { var containerElement = document.getElementById("container"); containerElement.innerHTML = ""; gTestMode = mode; gTestProperty = property; gShouldForce3D = shouldForce3D; gOpaque = opaque; gFrameCount = 0; createBoxes(); window.setTimeout((function() { gStartTime = (((new Date).getTime()) / 1000.0); if(gTestMode == "transition") { window.requestAnimFrame(kickoffTransition); } else if(gTestMode == "javascript") { window.requestAnimFrame(heartbeat); } }), 2000); } function endTest(frames, elapsedTime) { //document.getElementById("output").innerHTML += "" + frames + " / " + elapsedTime + " = " + (frames/elapsedTime) + "<br>"; runNextTest(); } function kickoffTransition() { for(var i = 0; i < gBoxes.length; i++) { setElementPosition(gBoxes[i].element, gBoxes[i].endX, gBoxes[i].endY); } window.setTimeout(function() { endTest(); }, (kTestDuration * 1000) + 1000); } function heartbeat() { gFrameCount += 1; var currentTime = (((new Date).getTime()) / 1000.0) - gStartTime; var percentComplete = currentTime / kTestDuration; for(var i = 0; i < gBoxes.length; i++) { xPos = Math.floor(gBoxes[i].startX + ((gBoxes[i].endX - gBoxes[i].startX) * percentComplete)); yPos = Math.floor(gBoxes[i].startY + ((gBoxes[i].endY - gBoxes[i].startY) * percentComplete)); setElementPosition(gBoxes[i].element, xPos, yPos); } if(currentTime < kTestDuration) { window.requestAnimFrame(heartbeat); } else { endTest(gFrameCount, currentTime); } } function setElementPosition(element, xPos, yPos) { if(gTestProperty == "topleft") { element.style.left = "" + xPos + "px"; element.style.top = "" + yPos + "px"; } else if(gTestProperty == "translate") { if(gShouldForce3D == true) { element.style[kTransformName] = "translateX(" + xPos + "px) translateY(" + yPos + "px) rotateY(0deg)"; } else { element.style[kTransformName] = "translateX(" + xPos + "px) translateY(" + yPos + "px)"; } } } function createBoxes() { var containerElement = document.getElementById("container"); gBoxes = Array(); for(var i = 0; i < kTotalBoxCount; i++) { var boxElement = document.createElement("div"); boxElement.style.border = "1px solid #333"; boxElement.style.backgroundColor = "#acf"; boxElement.style.width = "" + kBoxWidth + "px"; boxElement.style.height = "" + kBoxHeight + "px"; if(gTestProperty == "translate") { boxElement.style.left = "0px"; boxElement.style.top = "0px"; } if(gTestMode == "transition") { if(gTestProperty == "topleft") { boxElement.style[kTransitionName] = "left " + kTestDuration + "s linear, top " + kTestDuration + "s linear"; } else if(gTestProperty == "translate") { boxElement.style[kTransitionName] = "" + kTransformName + " " + kTestDuration + "s linear"; } } boxElement.style.position = "absolute"; if(gOpaque == false) { boxElement.style.opacity = 1.0 - (i / kTotalBoxCount); } if(gShouldForce3D == true) { boxElement.style[kTransformName] = "rotateY(0deg)"; } boxElement.id = "box-" + i; var startX = Math.floor(Math.random() * (kContainerWidth - kBoxWidth)); var startY = Math.floor(Math.random() * (kContainerHeight - kBoxHeight)); var endX = Math.floor(Math.random() * (kContainerWidth - kBoxWidth)); var endY = Math.floor(Math.random() * (kContainerHeight - kBoxHeight)); gBoxes.push({"startX" : startX, "startY" : startY, "endX" : endX, "endY" : endY , "element" : boxElement}); containerElement.appendChild(boxElement); setElementPosition(boxElement, startX, startY); } } </script> </head> <body onload = "beginTesting()"> <div id = "container"></div> <div id = "output"></div> </body> <html>