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; | |
| } |