Click to refresh.
A Pen by satchmorun on CodePen.
Click to refresh.
A Pen by satchmorun on CodePen.
<canvas id="canvas"></canvas> |
var canvas = document.getElementById('canvas'); | |
var ctx = canvas.getContext('2d'); | |
var W, H; | |
sizeCanvas(); | |
var raf = requestAnimationFrame; | |
/*---------------------------------------------------------------------------*/ | |
'floor|random|round|abs|sqrt|PI|atan2|sin|cos|pow|max|min' | |
.split('|') | |
.forEach(function(p) { this[p] = Math[p]; }); | |
var TAU = PI*2; | |
function randint(n) { return floor(n*random()); } | |
function choose() { return arguments[randint(arguments.length)]; } | |
/*---------------------------------------------------------------------------*/ | |
var running = false; | |
var time = 0; | |
function sizeCanvas() { | |
W = canvas.width = innerWidth; | |
H = canvas.height = innerHeight; | |
} | |
function loop() { | |
if (running) raf(loop); | |
draw(); | |
time++; | |
} | |
document.onclick = function(e) { | |
running = false; | |
setTimeout(function() { | |
time = 0; | |
reset(); | |
running = true; | |
raf(loop); | |
}, 100); | |
}; | |
/*---------------------------------------------------------------------------*/ | |
function Creeper(x, y, fn, depth) { | |
this.x = x; | |
this.y = y; | |
this.fn = (fn || chooseDirection()); | |
this.depth = (depth || 0); | |
} | |
var directions = [ | |
N, N, S, S, E, E, W_, W_, | |
NE1, NE2, NW1, NW2, | |
SE1, SE2, SW1, SW2 | |
]; | |
function chooseDirection(fns) { | |
if (fns) { | |
do { fn = chooseDirection(); } while(fns.indexOf(fn) > -1); | |
return fn; | |
} | |
return directions[randint(16)]; | |
} | |
Creeper.prototype.animate = function(n) { | |
this.fn(this.x, this.y, n); | |
}; | |
Creeper.prototype.spawn = function() { | |
var x, y; | |
switch (this.fn) { | |
case N: x = this.x; y = this.y - Length; break; | |
case S: x = this.x; y = this.y + Length; break; | |
case E: x = this.x + Length; y = this.y; break; | |
case W_: x = this.x - Length; y = this.y; break; | |
case NE1: case NE2: | |
x = this.x + Length; y = this.y - Length; break; | |
case NW1: case NW2: | |
x = this.x - Length; y = this.y - Length; break; | |
case SE1: case SE2: | |
x = this.x + Length; y = this.y + Length; break; | |
case SW1: case SW2: | |
x = this.x - Length; y = this.y + Length; break; | |
} | |
var dir = chooseDirection(); | |
return new Creeper(x, y, dir, this.depth+1); | |
}; | |
function N(x, y, n) { segment(x, y, x, y - n*Length); } | |
function S(x, y, n) { segment(x, y, x, y + n*Length); } | |
function E(x, y, n) { segment(x, y, x + n*Length, y); } | |
function W_(x, y, n) { segment(x, y, x - n*Length, y); } | |
function NE1(x, y, n) { arc(x, y-Length, TAU/4, 0, n, true); } | |
function NE2(x, y, n) { arc(x+Length, y , PI, 3*TAU/4, n, false); } | |
function NW1(x, y, n) { arc(x , y-Length, TAU/4, TAU/2, n, false); } | |
function NW2(x, y, n) { arc(x-Length, y , TAU, 3*TAU/4, n, true); } | |
function SE1(x, y, n) { arc(x , y+Length, 3*TAU/4, TAU, n, false); } | |
function SE2(x, y, n) { arc(x+Length, y , TAU/2, TAU/4, n, true); } | |
function SW1(x, y, n) { arc(x , y+Length, 3*TAU/4, TAU/2, n, true); } | |
function SW2(x, y, n) { arc(x-Length, y , 0, TAU/4, n, false); } | |
function jitter(n) { | |
return n + choose(-1, 0, 1); | |
} | |
function segment(x1, y1, x2, y2) { | |
x1 = jitter(x1); | |
x2 = jitter(x2); | |
y1 = jitter(y1); | |
y2 = jitter(y2); | |
ctx.beginPath(); | |
ctx.moveTo(x1, y1); | |
ctx.lineTo(x2, y2); | |
ctx.lineWidth = Thickness; | |
ctx.strokeStyle = Color; | |
ctx.stroke(); | |
} | |
function arc(x, y, a1, a2, n, dir) { | |
x = jitter(x); | |
y = jitter(y); | |
ctx.beginPath(); | |
ctx.arc(x, y, Length, a1, n*(a2-a1)+a1, dir); | |
ctx.lineWidth = Thickness; | |
ctx.strokeStyle = Color; | |
ctx.stroke(); | |
} | |
/*---------------------------------------------------------------------------*/ | |
var creepers; | |
var drawn; | |
var Length = 40; | |
var Thickness = 0.2; | |
var Color = 'rgba(23, 23, 23, 0.8);' | |
// Frames per cycle | |
var FPC = 20; | |
function reset() { | |
sizeCanvas(); | |
ctx.clearRect(0, 0, W, H); | |
creepers = [new Creeper(floor(W/2), floor(H/2), N), | |
new Creeper(floor(W/2), floor(H/2), S), | |
new Creeper(floor(W/2), floor(H/2), E), | |
new Creeper(floor(W/2), floor(H/2), W_)]; | |
} | |
function draw() { | |
var next = []; | |
for (var i = 0; i < creepers.length; i++) { | |
var c = creepers[i]; | |
c.animate((time % FPC) / FPC); | |
} | |
if (time > 0 && time % FPC === 0) { | |
for (i = 0; i < creepers.length; i++) { | |
var c = creepers[i]; | |
if (c.depth > 8) continue; | |
next.push(c.spawn()); | |
if (random() < 0.1) next.push(c.spawn()); | |
} | |
creepers = next; | |
} | |
} | |
reset(); | |
running = true; | |
raf(loop); |
body { | |
margin: 0; | |
padding: 0; | |
overflow: hidden; | |
} |