console.clear(); var extend = function ( defaults, options ) { var extended = {}; var prop; for (prop in defaults) { if (Object.prototype.hasOwnProperty.call(defaults, prop)) { extended[prop] = defaults[prop]; } } for (prop in options) { if (Object.prototype.hasOwnProperty.call(options, prop)) { extended[prop] = options[prop]; } } return extended; }; var TemplateShape = function (p) { // returns a cached canvas symbol var ts = this; ts.size = p.c.global.size * p.scale; ts.width = p.c.global.weight * p.scale; ts.canvas = document.createElement('canvas'); ts.canvas.width = ts.canvas.height = ts.size; ts.ctx = ts.canvas.getContext("2d"); ts.ctx.fillStyle = p.c.global.fill; ts.ctx.fillRect( Math.round((ts.size * 0.5) - (ts.width * 0.5)), 0, Math.round(ts.width), Math.round(ts.size) ); ts.ctx.fillRect( 0, Math.round((ts.size * 0.5) - (ts.width * 0.5)), Math.round(ts.size), Math.round(ts.width) ); return ts; }; var Particle = function (c) { // returns a particle controller var p = this; p.c = c; p.last = 0; // save the index to break wiggle uniformity p.index = c.particles.length; // scale controls the particle's size and in a parallax-like manner p.scale = Math.random() + 0.5 * 0.5; // assign random starting position p.y = Math.floor(Math.random() * p.c.canvas.height); p.x = Math.floor(Math.random() * p.c.canvas.width); // create a cached symbol p.shape = new TemplateShape(p); p.draw = function () { p.c.ctx.drawImage( p.shape.canvas, Math.round(p.x), // particles should flow up, hence the subtraction Math.round(p.c.canvas.height - p.y)); }; p.update = function (t) { // apply vertical movement p.delta = t - p.last; p.y = p.y + (p.c.global.speed * p.scale * (p.delta)); // particle gone beyond canvas' edges? if(p.y - p.shape.size > p.c.canvas.height) { // move it back to the opposite edge p.y = 0 - p.shape.canvas.height; p.x = Math.floor(Math.random() * p.c.canvas.width); } // apply wiggle in x-axis p.x = p.x + (Math.sin( p.index + (t / p.c.global.wiggleSpeed ) ) * p.scale * p.c.global.wiggle); p.last = t; }; p.handleResize = function (c) { //console.log(c.canvas.width); }; return p; }; var Cloud = function (options) { var c = this; c.canvas = document.getElementById("bg"); c.canvas.width = window.innerWidth; c.canvas.height = window.innerHeight; c.ctx = c.canvas.getContext("2d"); c.global = { paused: false, amount: 33, // total amount of particles size: 100, // base size of particles weight: 33, // thickness of the symbol speed: 0.025, // base speed of particles // particle color should match color defined in canvas' CSS // NOTE: color is not dynamic fill: window.getComputedStyle(c.canvas).color, wiggle: 0.5, // base wiggle amount wiggleSpeed: 1000 // base wiggle speed (bigger is slower) }; c.global = extend(c.global, options); c.particles = []; // individual Particles are stored here for(var p = 0; p < c.global.amount; p++) { c.particles.push(new Particle(c)); } c.update = function (time) { // set initial time, so that no argument is required for the first call if(typeof time === "undefined") { time = 0; } // clear the canvas c.ctx.clearRect(0, 0, c.canvas.width, c.canvas.height); c.particles.forEach(function(e) { e.update(time); }); c.particles.forEach(function(e) { e.draw(); }); c.raf = window.requestAnimationFrame(c.update); }; c.handleResize = function () { c.canvas.width = window.innerWidth; c.canvas.height = window.innerHeight; }; c.pause = function () { if(c.global.paused) { c.raf = window.requestAnimationFrame(c.update); c.canvas.classList.remove("paused"); c.global.paused = false; } else { window.cancelAnimationFrame(c.raf); c.canvas.classList.add("paused"); c.global.paused = true; } }; window.addEventListener("resize", c.handleResize); c.canvas.addEventListener("click", c.pause); return c; }; var c = new Cloud(); c.update();