var canvas, ctx, width, height, size, lines, tick; function line() { this.path = []; this.speed = rand(10, 20); this.count = randInt(10, 30); this.x = width / 2, +1; this.y = height / 2 + 1; this.target = { x: width / 2, y: height / 2 }; this.dist = 0; this.angle = 0; this.hue = tick / 5; this.life = 1; this.updateAngle(); this.updateDist(); } line.prototype.step = function(i) { this.x += Math.cos(this.angle) * this.speed; this.y += Math.sin(this.angle) * this.speed; this.updateDist(); if (this.dist < this.speed) { this.x = this.target.x; this.y = this.target.y; this.changeTarget(); } this.path.push({ x: this.x, y: this.y }); if (this.path.length > this.count) { this.path.shift(); } this.life -= 0.001; if (this.life <= 0) { this.path = null; lines.splice(i, 1); } }; line.prototype.updateDist = function() { var dx = this.target.x - this.x, dy = this.target.y - this.y; this.dist = Math.sqrt(dx * dx + dy * dy); } line.prototype.updateAngle = function() { var dx = this.target.x - this.x, dy = this.target.y - this.y; this.angle = Math.atan2(dy, dx); } line.prototype.changeTarget = function() { var randStart = randInt(0, 3); switch (randStart) { case 0: // up this.target.y = this.y - size; break; case 1: // right this.target.x = this.x + size; break; case 2: // down this.target.y = this.y + size; break; case 3: // left this.target.x = this.x - size; } this.updateAngle(); }; line.prototype.draw = function(i) { ctx.beginPath(); var rando = rand(0, 10); for (var j = 0, length = this.path.length; j < length; j++) { ctx[(j === 0) ? 'moveTo' : 'lineTo'](this.path[j].x + rand(-rando, rando), this.path[j].y + rand(-rando, rando)); } ctx.strokeStyle = 'hsla(' + rand(this.hue, this.hue + 30) + ', 80%, 55%, ' + (this.life / 3) + ')'; ctx.lineWidth = rand(0.1, 2); ctx.stroke(); }; function rand(min, max) { return Math.random() * (max - min) + min; } function randInt(min, max) { return Math.floor(min + Math.random() * (max - min + 1)); }; function init() { canvas = document.getElementById('canvas'); ctx = canvas.getContext('2d'); size = 30; lines = []; reset(); loop(); } function reset() { width = Math.ceil(window.innerWidth / 2) * 2; height = Math.ceil(window.innerHeight / 2) * 2; tick = 0; lines.length = 0; canvas.width = width; canvas.height = height; } function create() { if (tick % 10 === 0) { lines.push(new line()); } } function step() { var i = lines.length; while (i--) { lines[i].step(i); } } function clear() { ctx.globalCompositeOperation = 'destination-out'; ctx.fillStyle = 'hsla(0, 0%, 0%, 0.1)'; ctx.fillRect(0, 0, width, height); ctx.globalCompositeOperation = 'lighter'; } function draw() { ctx.save(); ctx.translate(width / 2, height / 2); ctx.rotate(tick * 0.001); var scale = 0.8 + Math.cos(tick * 0.02) * 0.2; ctx.scale(scale, scale); ctx.translate(-width / 2, -height / 2); var i = lines.length; while (i--) { lines[i].draw(i); } ctx.restore(); } function loop() { requestAnimationFrame(loop); create(); step(); clear(); draw(); tick++; } function onresize() { reset(); } window.addEventListener('resize', onresize); init();