Skip to content

Instantly share code, notes, and snippets.

@hacst
Last active August 29, 2015 14:14
Show Gist options
  • Save hacst/914f1abb9f28acc285fd to your computer and use it in GitHub Desktop.
Save hacst/914f1abb9f28acc285fd to your computer and use it in GitHub Desktop.
Test Canvas based animation of a signal on a line
<html>
<style>
body {
}
#canvas {
width: 100%;
height: 100%;
margin: 0px;
}
</style>
<head>
<title>Line animation test using canvas</title>
</head>
<body>
<canvas id="circuit"></canvas>
<script type="text/javascript">
var px_per_ms = 0.1;
var lines = [];
function dt_next_toggle() {
return Math.random() * 2000;
}
var canvas = document.getElementById("circuit");
function add_toggly_line(x, y, offsets) {
var length = 0;
for (var i = 0; i < offsets.length; ++i) {
length += offsets[i];
}
var data = {
'sx': x,
'sy': y,
'offsets': offsets,
'impulses': [Number.NEGATIVE_INFINITY],
'state': false,
'length': length
};
lines.push(data);
function toggle() {
data.state = !data.state;
data.impulses.push(window.performance.now())
setTimeout(toggle, dt_next_toggle());
}
setTimeout(toggle, dt_next_toggle());
return data;
}
for (var o = 0; o < 2; ++o) {
for (var i = 0; i < 100; ++i) {
add_toggly_line(1000 - i * 10 + 5 + o * 500, i * 10 + 5, [100, 100, 100, 100], 400);
}
}
function dashes_for(timestamp, line) {
var dashes = [];
var cutoff = -1;
if (line.state == false) {
// If the state is false we need the first impulse to be interpreted as a gap
dashes.push(0);
}
var end_of_last_px = 0;
var max_runtime = line.length / px_per_ms; // Time for signal to traverse
for (var i = line.impulses.length - 1; i >= 0; --i) {
var dt = Math.max(0.0, timestamp - line.impulses[i]); // Workaround Chrome giving negative dt
if (dt > max_runtime) {
// Signal no longer relevant, can mark it and rest for deletion
cutoff = i;
break;
}
var till_px = dt * px_per_ms;
dashes.push(till_px - end_of_last_px); //TODO: Figure out of rounding here saves time
end_of_last_px = till_px;
}
dashes.push(line.length); // Simply make sure that whatever is at the end it extends the rest of the wire
if (cutoff > 50) {
// Amortized clean-up so we don't constantly shift the cheap vector
line.impulses.splice(0, cutoff + 1);
}
return dashes;
}
function pathLine(ctx, line) {
var x = line.sx;
var y = line.sy;
ctx.moveTo(x, y);
for (var j = 0; j < line.offsets.length; ++j) {
var offset = line.offsets[j];
if (offset == 0)
continue;
if (j % 2 == 0) {
x += offset;
} else {
y += offset;
}
ctx.lineTo(x, y);
}
}
function drawFrame(timestamp) {
var ctx = canvas.getContext("2d");
ctx.canvas.width = 2000; //window.innerWidth; // For now go fixed width
ctx.canvas.height = 2000; //window.innerHeight;
// Draw the background lines in one go
// Pretty sure we can save drawing those from scratch every frame quite
// easily. For now making it worst case is good though.
ctx.save();
ctx.beginPath();
ctx.lineWidth = 2;
for (var i = 0; i < lines.length; ++i) {
var line = lines[i];
pathLine(ctx, line);
}
ctx.stroke();
ctx.restore();
// Now draw the animation
ctx.save();
ctx.lineWidth = 2;
ctx.strokeStyle = 'red';
for (var i = 0; i < lines.length; ++i) {
// Animate all lines
var line = lines[i];
if (line.impulses.length == 0) // Line is steady state. Skip
continue;
ctx.beginPath();
ctx.setLineDash(dashes_for(timestamp, line));
pathLine(ctx, line);
ctx.stroke();
}
ctx.restore();
requestAnimationFrame(drawFrame);
}
requestAnimationFrame(drawFrame);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment