Toggle navigation
Sign Up
Log In
Explore
Works
Folders
Tools
Collections
Artists
Groups
Groups
Topics
Tasks
Tasks
Jobs
Teams
Jobs
Recommendation
More Effects...
JS
// Critical Mass Particle Symulation // cross browser requestAnimationFrame loop to handle the animation looping window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); }; })(); var cm = (function(){ var data = { baseAttractRadius: 80, startingHue: 180, hueRange: -200, criticalMass: 0.8 }; // average particles per 100x100 pixel square var particleDensity = 2, winWidth, winHeight, totalParticles = 0, particles = [], ctx, baseRenderSpeed = 60, renderTimer = (new Date()).getTime(), FPS = 0, mX = 0, mY = 0, mouseIsDown = false, exploding = 0, explodeTime = 200, canvas; // ------------------------ // // #### Particle Class #### // // ------------------------ // function Particle(id){ var self = this; self.id = id; // set this particle's initial location to a random // place on the screen within the bounds of the window. self.x = Math.round(Math.random()*winWidth); self.y = Math.round(Math.random()*winHeight); // track how many particles this one is currently // attracting. This will be set in our update method. self.attracting = 0; // this will serve as a temporary holding bin for // how many particles we are attracting. // updated by the update function, and then used // to fill the main attracting value. self.attractingNext = 0; // We want each particle to adjust its color based on how many // other particles it is attracting, this sets the start value self.hue = data.startingHue; self.af = 1 / totalParticles; // set the radius around the particle within which it will // attract other particles. we're using the startingAttractionRadius // as our starting point. self.attractionRadius = data.baseAttractRadius; // the radius of this particle self.radius = 15; // start this particle with no velocity self.velx = Math.random() * 2 - 1; self.vely = Math.random() * 2 - 1; } // Setup a common draw function that will be called // by all members of the Particle class Particle.prototype.draw = function(){ var self = this; var totalRadius = self.attractionRadius/2; var radiusFrac = self.radius/totalRadius; var _gradient = ctx.createRadialGradient(self.x, self.y, 3, self.x, self.y, totalRadius); _gradient.addColorStop(0, "hsla(" + self.hue + ",100%,50%," + (self.af*0.9+0.1) + ")"); _gradient.addColorStop(radiusFrac, "hsla(" + self.hue + ",100%,50%,0)"); _gradient.addColorStop(radiusFrac+0.05, "hsla(" + self.hue + ",100%,50%," + (self.af*0.2+0.05) + ")"); _gradient.addColorStop(radiusFrac+0.06, "hsla(" + self.hue + ",100%,50%,0)"); _gradient.addColorStop(0.8, "hsla(" + self.hue + ",100%,50%,0)"); _gradient.addColorStop(1, "hsla(" + self.hue + ",100%,80%,0.017)"); ctx.fillStyle = _gradient; ctx.beginPath(); ctx.arc(self.x, self.y, totalRadius, 0, Math.PI * 2, false); ctx.fill(); }; // ------------------------------ // // #### Draw the canvas data #### // // ------------------------------ // function drawScene(){ // update the scene update(); // draw the black background if(exploding){ ctx.fillStyle = "rgba(0,0,0," + (1 - exploding / explodeTime) + ")"; }else{ ctx.fillStyle = "rgba(0,0,0,1)"; } ctx.fillRect(0, 0, winWidth, winHeight); // draw each particle for(var i=0; i
5) _timeDeviation = 5; // set our renderTimer to now in preparation for next // time we update. renderTimer = _timer; if(exploding) exploding--; var _boom = exploding ? -1.5 : 1; // look at each particle and evaluate how it should move // next based on it's current valocity and attraction to // any other particles that are close enough. for(var i=0; i < particles.length; i++){ var _p1 = particles[i]; _p1.x += _p1.velx * _timeDeviation; _p1.y += _p1.vely * _timeDeviation; if(_p1.af > data.criticalMass && !exploding){ exploding = explodeTime; } // check if other particles are affecting this particle // we use i+1 because we only need to check the // relationship between 2 particles once. for(var ii=i+1; ii < particles.length; ii++){ var _p2 = particles[ii]; // to get the distance between our 2 points we need // to calculate the difference for the x and y axis // then get the length of the hypotenuse // more info at: http://en.wikipedia.org/wiki/Pythagorean_theorem var _dx = _p2.x - _p1.x; var _dy = _p2.y - _p1.y; var _distance = Math.sqrt(_dx*_dx + _dy*_dy); // attractAmt is the amount of attraction between the 2 points // numberOfAttrators tells whether both particles are attracting // each other, or if only one is. We need this because a particle's // attraction Radius is dependant on the number of particles it // is within range of. var attractAmt = 0, numberOfAttractors = 0; if(_distance < _p1.attractionRadius) { attractAmt += (_p1.attractionRadius - _distance) / _p1.attractionRadius; attractAmt *= 1 - (_p1.af * 0.85); numberOfAttractors++; _p1.attractingNext++; } if(_distance < _p2.attractionRadius) { attractAmt += (_p2.attractionRadius - _distance) / _p2.attractionRadius; attractAmt *= 1 - (_p2.af * 0.85); numberOfAttractors++; _p2.attractingNext++; } // if either particle is attracting the other, make our adjustments if(numberOfAttractors > 0){ // cut the attraction amount in 1/2 if only one is attracting attractAmt *= numberOfAttractors/2; // adjust for how fast our FPS clock is moving attractAmt *= _timeDeviation; // _dx and _dy are used because we want to attract along the x and // y axis proportionately. we divide by 2000 to slow things a bit var _vx = _dx*attractAmt/2000; var _vy = _dy*attractAmt/2000; _p1.velx += _vx * _boom; _p1.vely += _vy * _boom; _p2.velx -= _vx * _boom; _p2.vely -= _vy * _boom; } } // We don't want to let the particles leave the // area, so just change their position when they // touch the walls of the window if(_p1.x + _p1.radius >= winWidth && _p1.velx > 0){ _p1.velx *= -0.8; } else if(_p1.x - _p1.radius < 0 && _p1.velx < 0) { _p1.velx *= -0.8; } if(_p1.y + _p1.radius > winHeight && _p1.vely > 0){ _p1.vely *= -0.8; } else if(_p1.y - _p1.radius < 0 && _p1.vely < 0) { _p1.vely *= -0.8; } if(mouseIsDown){ var _mdx = _p1.x - mX; var _mdy = _p1.y - mY; _mdist = Math.sqrt(_mdx*_mdx + _mdy*_mdy); var _maxDist = winWidth > winHeight ? winHeight : winWidth; if(_mdist < _maxDist){ var _af = (_maxDist-_mdist)/_maxDist; _p1.velx -= (_mdx*_af)/1000; _p1.vely -= (_mdy*_af)/1000; } } // We now know all the particles that p1 is attracting // so we can set that value for use on the next update _p1.attracting = _p1.attractingNext; // and clear it out so it's ready to use next time. _p1.attractingNext = 0; // what fraction of the overall particles are we attracting? // I call it the attraction fraction (af) _p1.af = (_p1.attracting + 1) / totalParticles; // set this particle's hue based on how many other // particles it is attracting. _p1.hue = data.startingHue + (_p1.af/data.criticalMass) * data.hueRange; // set the radius around the particle within which it will // attract other particles. we're using the startingAttractionRadius // and adding 1 pixel more for each particle we are already attracting. _p1.attractionRadius = data.baseAttractRadius + (_p1.attracting); } } // -------------------------------------------- // // #### Initialize and setup the animation #### // // -------------------------------------------- // function init(){ // Get a reference to our canvas canvas = document.createElement("canvas"); // add the canvas to the document document.body.appendChild(canvas); // Get the 2d drawing surface of the canvas ctx = canvas.getContext("2d"); resize(); window.addEventListener( 'resize', resize, false ); totalParticles = Math.round(winHeight/100) * Math.round(winWidth/100) * particleDensity; // create all of our particles and push them into our main array to hold. for(var i = 0; i < totalParticles; i++){ particles.push(new Particle(i)); } drawScene(); bindEvents(); } function resize(){ winHeight = window.innerHeight; winWidth = window.innerWidth; canvas.width = winWidth; canvas.height = winHeight; canvas.style.width = winWidth + 'px'; canvas.style.height = winHeight + 'px'; } // -------------------------------------------------- // // #### Set up the event bindings for user input #### // // -------------------------------------------------- // function bindEvents(){ // Track the mouse position window.addEventListener('mousemove', function(e){ mX = e.x; mY = e.y; }, false); // track if the mouse is being pressed window.addEventListener('mousedown', function(e){ mouseIsDown = true; }, false); // and when the mouse is released window.addEventListener('mouseup', function(e){ mouseIsDown = false; }, false); } init(); return { data: data }; })(); var gui = new dat.GUI(); gui.add(cm.data, 'baseAttractRadius').min(50).max(150).step(1); gui.add(cm.data, 'startingHue').min(0).max(320).step(1); gui.add(cm.data, 'hueRange').min(-320).max(320).step(1); gui.add(cm.data, 'criticalMass').min(0.1).max(1).step(0.1); gui.close();
CSS
body { padding: 0; margin: 0; overflow: hidden; height: 100%; width: 100%; } html { height: 100%; width: 100%; }
HTML
Join Effecthub.com
Working with Global Gaming Artists and Developers!
Login
Sign Up
Or Login with Your Email Address:
Email
Password
Remember
Or Sign Up with Your Email Address:
Your Email
This field must contain a valid email
Set Password
Password should be at least 1 character
Stay informed via email