Skip to content

Instantly share code, notes, and snippets.

@stepheneb
Last active April 6, 2023 21:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stepheneb/1299659 to your computer and use it in GitHub Desktop.
Save stepheneb/1299659 to your computer and use it in GitHub Desktop.
Canvas Animate Path Benchmark
<!DOCTYPE html>
<html>
<head>
<title>Canvas Animate Path Benchmark</title>
<style type="text/css">
body { margin: 0px 10px; }
h1 { font-size: 1.3em; line-height: 1.0em; }
table { font-size: 0.8em; margin: 0px 10px; padding: 2px }
th { text-align: center; font-weight: bold; padding: 0px 10px 0px 10px; }
td { text-align: center; font-weight: normal; padding: 0px 10px 0px 10px; }
ul.hlist { display: inline-block; list-style-type: none; margin: 0px; padding-left: 15px; }
ul.hlist li { display: inline-table; vertical-align: top; list-style-type: none }
#canvas { width: 400px; height: 400px; padding:0px;
background-color: #eeeeee; color:gray; border: solid 1px #cccccc }
</style>
<script type="text/javascript">
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
return window.setTimeout(callback, 1000/60);
};
})();
window.cancelRequestAnimFrame = (function() {
return window.cancelCancelRequestAnimationFrame ||
window.webkitCancelRequestAnimationFrame ||
window.mozCancelRequestAnimationFrame ||
window.oCancelRequestAnimationFrame ||
window.msCancelRequestAnimationFrame ||
window.clearTimeout;
})();
</script>
</head>
<body>
<h1>Canvas Animate Path Benchmark</h1>
<p>Measure performance of canvas line drawing speed by repeatedly re-drawing a 5000 element line.</p>
<ul class="hlist">
<li><canvas id="canvas"></canvas></li>
<li>
<form id="animate-controller">
<fieldset>
<legend>Step</legend>
<label><input type="radio" name="step" value="stop" checked> Stop</input></label>
<label><input type="radio" name="step" value="step"> Step</input></label>
<label><input type="radio" name="step" value="go"> Go</input></label>
<label><input type="radio" name="step" value="reset"> Reset</input></label>
</fieldset>
</form>
<p>frame: <span id="frame"></span></p>
<p>framerate: <span id="framerate"></span></p>
<p><i>Animation will stop automatically after 250 frames.</i></p>
</li>
</ul>
<p id="user-agent"></p>
<script type="text/javascript">
window.onload=function() {
var user_agent = document.getElementById("user-agent");
user_agent.innerHTML = navigator.userAgent;
var canvas = document.getElementById("canvas");
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
var animateRequest;
var animate_controller = document.getElementById("animate-controller");
var animate_controller_inputs = animate_controller.getElementsByTagName("input")
var frame = document.getElementById("frame");
var framerate = document.getElementById("framerate");
var animating;
var start_time, step_time;
var loop_start, loop_time, loop_elapsed;
var half_width = canvas.width / 2;
var half_height = canvas.height / 2;
var xpos = half_width;
var ypos = half_height;
var pos;
var path = [];
var ctx = canvas.getContext('2d');
ctx.globalCompositeOperation = "destination-atop";
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(255,65,0, 1.0)";
var i, j;
var factor = 15;
var factor_div_2 = factor / 2;
var correction = 0.002;
var transform_factor = 5;
var transform_factor_div_2 = transform_factor / 2;
var transform_correction = 0.00035;
var count, max_count;
var run_mode;
function init() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
count = 0;
max_count = 250;
xpos = half_width;
ypos = half_height;
path[0] = [xpos, ypos];
for (i=1; i < 5000; i++) {
xpos += (factor * Math.random() - factor_div_2) + (half_width - xpos) * correction;
ypos += (factor * Math.random() - factor_div_2) + (half_height - ypos) * correction;
path[i] = [xpos, ypos];
};
};
function drawPath() {
ctx.beginPath();
pos = path[0];
ctx.moveTo(pos[0], pos[1]);
for (var i=1; i < 5000; i++) {
pos = path[i];
ctx.lineTo(pos[0], pos[1]);
};
ctx.closePath();
ctx.stroke();
};
function transformPath() {
for (i=1; i < 5000; i++) {
path[i][0] += (transform_factor * Math.random() - transform_factor_div_2) + (half_width - xpos) * -correction;
path[i][1] += (transform_factor * Math.random() - transform_factor_div_2) + (half_height - ypos) * -correction;
};
};
function animateController() {
for(var i = 0; i < this.elements.length; i++)
if (this.elements[i].checked) run_mode = this.elements[i].value;
switch(run_mode) {
case "stop":
animateStop();
break;
case "step":
if (animateRequest) cancelRequestAnimFrame(animateRequest);
animating = false;
if (count < max_count) runAnimateStep();
animate_controller_inputs[0].checked = true;
break;
case "go":
start_time = +new Date();
animating = true;
if (count >= max_count) {
init();
drawPath();
animateRequest = requestAnimFrame(animateLoop, canvas);
} else {
animateRequest = requestAnimFrame(animateLoop, canvas);
};
break;
case "reset":
animateStop();
init();
drawPath();
break;
}
};
function animateStop() {
if (animateRequest) cancelRequestAnimFrame(animateRequest);
animating = false;
animate_controller_inputs[0].checked = true;
};
function runAnimateStep() {
count++;
ctx.clearRect(0, 0, canvas.width, canvas.height);
transformPath();
drawPath();
frame.innerHTML = count;
step_time = +new Date();
framerate.innerHTML = ~~(10000 / ((step_time - start_time) / count)) / 10;
// see: http://jsperf.com/rounding-numbers-down
// I'm also rendering with one digit to the right of the decimal point
};
function animateLoop(){
if (count < max_count && animating) {
loop_start = +new Date();
animateRequest = requestAnimFrame(animateLoop, canvas);
runAnimateStep();
loop_time = +new Date()
loop_elapsed = loop_time - loop_start;
while (loop_elapsed < 15) {
runAnimateStep();
loop_time = +new Date()
loop_elapsed = loop_time - loop_start;
}
} else {
animateStop()
}
};
animate_controller.onchange = animateController;
init();
drawPath();
};
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment