/* ----------------------------------------------- /* author : vincent garreau - vincentgarreau.com /* mit license: http://opensource.org/licenses/mit /* github : https://github.com/vincentgarreau/particles.js /* how to use? : check the github readme /* v1.0.3 /* ----------------------------------------------- */ function launchparticlesjs(tag_id, params){ var canvas_el = document.queryselector('#'+tag_id+' > canvas'); /* particles.js variables with default values */ pjs = { canvas: { el: canvas_el, w: canvas_el.offsetwidth, h: canvas_el.offsetheight }, particles: { color: '#fff', shape: 'circle', opacity: 1, size: 2.5, size_random: true, nb: 200, line_linked: { enable_auto: true, distance: 100, color: '#fff', opacity: 1, width: 1, condensed_mode: { enable: true, rotatex: 65000, rotatey: 65000 } }, anim: { enable: true, speed: 1 }, array: [] }, interactivity: { enable: true, mouse: { distance: 100 }, detect_on: 'canvas', mode: 'grab', line_linked: { opacity: 1 }, events: { onclick: { enable: true, mode: 'push', nb: 4 } } }, retina_detect: false, fn: { vendors:{ interactivity: {} } } }; /* params settings */ if(params){ if(params.particles){ var paramsforparticles = params.particles; if(paramsforparticles.color) pjs.particles.color = paramsforparticles.color; if(paramsforparticles.shape) pjs.particles.shape = paramsforparticles.shape; if(paramsforparticles.opacity) pjs.particles.opacity = paramsforparticles.opacity; if(paramsforparticles.size) pjs.particles.size = paramsforparticles.size; if(paramsforparticles.size_random == false) pjs.particles.size_random = paramsforparticles.size_random; if(paramsforparticles.nb) pjs.particles.nb = paramsforparticles.nb; if(paramsforparticles.line_linked){ var paramsforlinelinked = paramsforparticles.line_linked; if(paramsforlinelinked.enable_auto == false) pjs.particles.line_linked.enable_auto = paramsforlinelinked.enable_auto; if(paramsforlinelinked.distance) pjs.particles.line_linked.distance = paramsforlinelinked.distance; if(paramsforlinelinked.color) pjs.particles.line_linked.color = paramsforlinelinked.color; if(paramsforlinelinked.opacity) pjs.particles.line_linked.opacity = paramsforlinelinked.opacity; if(paramsforlinelinked.width) pjs.particles.line_linked.width = paramsforlinelinked.width; if(paramsforlinelinked.condensed_mode){ var paramsforcondensedmode = paramsforlinelinked.condensed_mode; if(paramsforcondensedmode.enable == false) pjs.particles.line_linked.condensed_mode.enable = paramsforcondensedmode.enable; if(paramsforcondensedmode.rotatex) pjs.particles.line_linked.condensed_mode.rotatex = paramsforcondensedmode.rotatex; if(paramsforcondensedmode.rotatey) pjs.particles.line_linked.condensed_mode.rotatey = paramsforcondensedmode.rotatey; } } if(paramsforparticles.anim){ var paramsforanim = paramsforparticles.anim; if(paramsforanim.enable == false) pjs.particles.anim.enable = paramsforanim.enable; if(paramsforanim.speed) pjs.particles.anim.speed = paramsforanim.speed; } } if(params.interactivity){ var paramsforinteractivity = params.interactivity; if(paramsforinteractivity.enable == false) pjs.interactivity.enable = paramsforinteractivity.enable; if(paramsforinteractivity.mouse){ if(paramsforinteractivity.mouse.distance) pjs.interactivity.mouse.distance = paramsforinteractivity.mouse.distance; } if(paramsforinteractivity.detect_on) pjs.interactivity.detect_on = paramsforinteractivity.detect_on; if(paramsforinteractivity.mode) pjs.interactivity.mode = paramsforinteractivity.mode; if(paramsforinteractivity.line_linked){ if(paramsforinteractivity.line_linked.opacity) pjs.interactivity.line_linked.opacity = paramsforinteractivity.line_linked.opacity; } if(paramsforinteractivity.events){ var paramsforevents = paramsforinteractivity.events; if(paramsforevents.onclick){ var paramsforonclick = paramsforevents.onclick; if(paramsforonclick.enable == false) pjs.interactivity.events.onclick.enable = false; if(paramsforonclick.mode != 'push') pjs.interactivity.events.onclick.mode = paramsforonclick.mode; if(paramsforonclick.nb) pjs.interactivity.events.onclick.nb = paramsforonclick.nb; } } } pjs.retina_detect = params.retina_detect; } /* convert hex colors to rgb */ pjs.particles.color_rgb = hextorgb(pjs.particles.color); pjs.particles.line_linked.color_rgb_line = hextorgb(pjs.particles.line_linked.color); /* detect retina */ if(pjs.retina_detect && window.devicepixelratio > 1){ pjs.retina = true; pjs.canvas.pxratio = window.devicepixelratio pjs.canvas.w = pjs.canvas.el.offsetwidth * pjs.canvas.pxratio; pjs.canvas.h = pjs.canvas.el.offsetheight * pjs.canvas.pxratio; pjs.particles.anim.speed = pjs.particles.anim.speed * pjs.canvas.pxratio; pjs.particles.line_linked.distance = pjs.particles.line_linked.distance * pjs.canvas.pxratio; pjs.particles.line_linked.width = pjs.particles.line_linked.width * pjs.canvas.pxratio; pjs.interactivity.mouse.distance = pjs.interactivity.mouse.distance * pjs.canvas.pxratio; } /* ---------- canvas functions ------------ */ pjs.fn.canvasinit = function(){ pjs.canvas.ctx = pjs.canvas.el.getcontext('2d'); }; pjs.fn.canvassize = function(){ pjs.canvas.el.width = pjs.canvas.w; pjs.canvas.el.height = pjs.canvas.h; window.onresize = function(){ if(pjs){ pjs.canvas.w = pjs.canvas.el.offsetwidth; pjs.canvas.h = pjs.canvas.el.offsetheight; /* resize canvas */ if(pjs.retina){ pjs.canvas.w *= pjs.canvas.pxratio; pjs.canvas.h *= pjs.canvas.pxratio; } pjs.canvas.el.width = pjs.canvas.w; pjs.canvas.el.height = pjs.canvas.h; /* repaint canvas */ pjs.fn.canvaspaint(); if(!pjs.particles.anim.enable){ pjs.fn.particlesremove(); pjs.fn.canvasremove(); launchparticles(); } } } }; pjs.fn.canvaspaint = function(){ pjs.canvas.ctx.fillrect(0, 0, pjs.canvas.w, pjs.canvas.h); }; pjs.fn.canvasremove = function(){ pjs.canvas.ctx.clearrect(0, 0, pjs.canvas.w, pjs.canvas.h); } /* --------- particles functions ----------- */ pjs.fn.particle = function(color, opacity, position){ /* position */ this.x = position ? position.x : math.random() * pjs.canvas.w; this.y = position ? position.y : math.random() * pjs.canvas.h; /* size */ this.radius = (pjs.particles.size_random ? math.random() : 1) * pjs.particles.size; if (pjs.retina) this.radius *= pjs.canvas.pxratio; /* color */ this.color = color; /* opacity */ this.opacity = opacity; /* animation - velocity for speed */ this.vx = -.5 + math.random(); this.vy = -.5 + math.random(); /* draw function */ this.draw = function(){ pjs.canvas.ctx.fillstyle = 'rgba('+this.color.r+','+this.color.g+','+this.color.b+','+this.opacity+')'; pjs.canvas.ctx.beginpath(); switch(pjs.particles.shape){ case 'circle': pjs.canvas.ctx.arc(this.x, this.y, this.radius, 0, math.pi * 2, false); break; case 'edge': pjs.canvas.ctx.rect(this.x, this.y, this.radius*2, this.radius*2); break; case 'triangle': pjs.canvas.ctx.moveto(this.x,this.y-this.radius); pjs.canvas.ctx.lineto(this.x+this.radius,this.y+this.radius); pjs.canvas.ctx.lineto(this.x-this.radius,this.y+this.radius); pjs.canvas.ctx.closepath(); break; } pjs.canvas.ctx.fill(); } }; pjs.fn.particlescreate = function(){ for(var i = 0; i < pjs.particles.nb; i++) { pjs.particles.array.push(new pjs.fn.particle(pjs.particles.color_rgb, pjs.particles.opacity)); } }; pjs.fn.particlesanimate = function(){ for(var i = 0; i < pjs.particles.array.length; i++){ /* the particle */ var p = pjs.particles.array[i]; /* move the particle */ p.x += p.vx * (pjs.particles.anim.speed/2); p.y += p.vy * (pjs.particles.anim.speed/2); /* change particle position if it is out of canvas */ if(p.x - p.radius > pjs.canvas.w) p.x = p.radius; else if(p.x + p.radius < 0) p.x = pjs.canvas.w + p.radius; if(p.y - p.radius > pjs.canvas.h) p.y = p.radius; else if(p.y + p.radius < 0) p.y = pjs.canvas.h + p.radius; /* check distance between each particle and mouse position */ for(var j = i + 1; j < pjs.particles.array.length; j++){ var p2 = pjs.particles.array[j]; /* link particles if enable */ if(pjs.particles.line_linked.enable_auto){ pjs.fn.vendors.distanceparticles(p,p2); } /* set interactivity if enable */ if(pjs.interactivity.enable){ /* interactivity mode */ switch(pjs.interactivity.mode){ case 'grab': pjs.fn.vendors.interactivity.grabparticles(p,p2); break; } } } } }; pjs.fn.particlesdraw = function(){ /* clear canvas */ pjs.canvas.ctx.clearrect(0, 0, pjs.canvas.w, pjs.canvas.h); /* move particles */ pjs.fn.particlesanimate(); /* draw each particle */ for(var i = 0; i < pjs.particles.array.length; i++){ var p = pjs.particles.array[i]; p.draw('rgba('+p.color.r+','+p.color.g+','+p.color.b+','+p.opacity+')'); } }; pjs.fn.particlesremove = function(){ pjs.particles.array = []; }; /* ---------- vendors functions ------------ */ pjs.fn.vendors.distanceparticles = function(p1, p2){ var dx = p1.x - p2.x, dy = p1.y - p2.y, dist = math.sqrt(dx*dx + dy*dy); /* check distance between particle and mouse mos */ if(dist <= pjs.particles.line_linked.distance) { /* draw the line */ var color_line = pjs.particles.line_linked.color_rgb_line; pjs.canvas.ctx.beginpath(); pjs.canvas.ctx.strokestyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+ (pjs.particles.line_linked.opacity-dist/pjs.particles.line_linked.distance) +')'; pjs.canvas.ctx.moveto(p1.x, p1.y); pjs.canvas.ctx.lineto(p2.x, p2.y); pjs.canvas.ctx.linewidth = pjs.particles.line_linked.width; pjs.canvas.ctx.stroke(); pjs.canvas.ctx.closepath(); /* condensed particles */ if(pjs.particles.line_linked.condensed_mode.enable){ var dx = p1.x - p2.x, dy = p1.y - p2.y, ax = dx/(pjs.particles.line_linked.condensed_mode.rotatex*1000), ay = dy/(pjs.particles.line_linked.condensed_mode.rotatey*1000); p2.vx += ax; p2.vy += ay; } } }; pjs.fn.vendors.interactivity.listeners = function(){ /* init el */ if(pjs.interactivity.detect_on == 'window'){ var detect_el = window; }else{ var detect_el = pjs.canvas.el; } /* el on mousemove */ detect_el.onmousemove = function(e){ if(detect_el == window){ var pos_x = e.clientx, pos_y = e.clienty; } else{ var pos_x = e.offsetx||e.clientx, pos_y = e.offsety||e.clienty; } if(pjs){ pjs.interactivity.mouse.pos_x = pos_x; pjs.interactivity.mouse.pos_y = pos_y; if(pjs.retina){ pjs.interactivity.mouse.pos_x *= pjs.canvas.pxratio; pjs.interactivity.mouse.pos_y *= pjs.canvas.pxratio; } pjs.interactivity.status = 'mousemove'; } }; /* el on onmouseleave */ detect_el.onmouseleave = function(e){ if(pjs){ pjs.interactivity.mouse.pos_x = 0; pjs.interactivity.mouse.pos_y = 0; pjs.interactivity.status = 'mouseleave'; } }; /* el on onclick */ if(pjs.interactivity.events.onclick.enable){ switch(pjs.interactivity.events.onclick.mode){ case 'push': detect_el.onclick = function(e){ if(pjs){ for(var i = 0; i < pjs.interactivity.events.onclick.nb; i++){ pjs.particles.array.push( new pjs.fn.particle( pjs.particles.color_rgb, pjs.particles.opacity, { 'x': pjs.interactivity.mouse.pos_x, 'y': pjs.interactivity.mouse.pos_y } ) ) } } } break; case 'remove': detect_el.onclick = function(e){ pjs.particles.array.splice(0, pjs.interactivity.events.onclick.nb); } break; } } }; pjs.fn.vendors.interactivity.grabparticles = function(p1, p2){ var dx = p1.x - p2.x, dy = p1.y - p2.y, dist = math.sqrt(dx*dx + dy*dy); var dx_mouse = p1.x - pjs.interactivity.mouse.pos_x, dy_mouse = p1.y - pjs.interactivity.mouse.pos_y, dist_mouse = math.sqrt(dx_mouse*dx_mouse + dy_mouse*dy_mouse); /* check distance between 2 particles + check distance between 1 particle and mouse position */ if(dist <= pjs.particles.line_linked.distance && dist_mouse <= pjs.interactivity.mouse.distance && pjs.interactivity.status == 'mousemove'){ /* draw the line */ var color_line = pjs.particles.line_linked.color_rgb_line; pjs.canvas.ctx.beginpath(); pjs.canvas.ctx.strokestyle = 'rgba('+color_line.r+','+color_line.g+','+color_line.b+','+ (pjs.interactivity.line_linked.opacity-dist_mouse/pjs.interactivity.mouse.distance) +')'; pjs.canvas.ctx.moveto(p1.x, p1.y); pjs.canvas.ctx.lineto(pjs.interactivity.mouse.pos_x, pjs.interactivity.mouse.pos_y); pjs.canvas.ctx.linewidth = pjs.particles.line_linked.width; pjs.canvas.ctx.stroke(); pjs.canvas.ctx.closepath(); } }; pjs.fn.vendors.destroy = function(){ cancelanimationframe(pjs.fn.requestanimframe); canvas_el.remove(); delete pjs; }; /* --------- launch ----------- */ function launchparticles(){ pjs.fn.canvasinit(); pjs.fn.canvassize(); pjs.fn.canvaspaint(); pjs.fn.particlescreate(); pjs.fn.particlesdraw(); }; function launchanimation(){ pjs.fn.particlesdraw(); pjs.fn.requestanimframe = requestanimframe(launchanimation); }; launchparticles(); if(pjs.particles.anim.enable){ launchanimation(); } if(pjs.interactivity.enable){ pjs.fn.vendors.interactivity.listeners(); } }; /* --- vendors --- */ window.requestanimframe = (function(){ return window.requestanimationframe || window.webkitrequestanimationframe || window.mozrequestanimationframe || window.orequestanimationframe || window.msrequestanimationframe || function(callback){ window.settimeout(callback, 1000 / 60); }; })(); window.cancelrequestanimframe = ( function() { return window.cancelanimationframe || window.webkitcancelrequestanimationframe || window.mozcancelrequestanimationframe || window.ocancelrequestanimationframe || window.mscancelrequestanimationframe || cleartimeout } )(); function hextorgb(hex){ // by tim down - http://stackoverflow.com/a/5624139/3493650 // expand shorthand form (e.g. "03f") to full form (e.g. "0033ff") var shorthandregex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hex = hex.replace(shorthandregex, function(m, r, g, b) { return r + r + g + g + b + b; }); var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseint(result[1], 16), g: parseint(result[2], 16), b: parseint(result[3], 16) } : null; }; /* --- launch --- */ window.particlesjs = function(tag_id, params){ /* no string id? so it's object params, and set the id with default id */ if(typeof(tag_id) != 'string'){ console.log(tag_id); params = tag_id; tag_id = 'particles-js'; } /* no id? set the id to default id */ if(!tag_id){ tag_id = 'particles-js'; } /* create canvas element */ var canvas_el = document.createelement('canvas'); /* set size canvas */ canvas_el.style.width = "100%"; canvas_el.style.height = "100%"; /* append canvas */ var canvas = document.getelementbyid(tag_id).appendchild(canvas_el); /* launch particle.js */ if(canvas != null){ launchparticlesjs(tag_id, params); } };