Toggle navigation
Sign Up
Log In
Explore
Works
Folders
Tools
Collections
Artists
Groups
Groups
Topics
Tasks
Tasks
Jobs
Teams
Jobs
Recommendation
More Effects...
JS
// Fire Cells 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 firecell = (function(){ // average particles per 4 grid blocks var winWidth, winHeight, gridCols, gridRows, totalParticles, particles, particleGrid, canvas, ctx, targetFPS = 60, timer = (new Date()).getTime(), FPS = 0, mX = 0, mY = 0, mouseIsDown = false, explodeTime = 200, tick = 1, initialized = false, initializing = true; var data = { startingHue: 180, hueRange: -200, explodeThreshold: 0.15, drawGrid: false, particleDensity: 3, gridSize: 50, init: init }; // ------------------------ // // #### Particle Class #### // // ------------------------ // function Particle(id){ // reference to the current particle var _p = this; _p.id = id; // set this particle's initial location to a random // place on the screen within the bounds of the window. _p.x = Math.round(Math.random()*(winWidth-1)); _p.y = Math.round(Math.random()*(winHeight-1)); // Set the particle's initial grid position _p.grid = { x: Math.floor(_p.x / data.gridSize), y: Math.floor(_p.y / data.gridSize) }; // helper method to set the particle's X and make sure // it's in the correct collision detection grid box _p.setX = function setX(x){ _p.x = x; var _oldGridX = _p.grid.x; _p.grid.x = Math.floor(_p.x / data.gridSize); if(_p.grid.x >= gridCols) _p.grid.x = gridCols-1; if(_p.grid.x < 0) _p.grid.x = 0; if(_p.grid.x !== _oldGridX){ delete particleGrid[_oldGridX][_p.grid.y][_p.id]; particleGrid[_p.grid.x][_p.grid.y][_p.id] = _p; } }; // helper method to set the particle's Y and make sure // it's in the correct collision detection grid box _p.setY = function setY(y){ _p.y = y; var _oldGridY = _p.grid.y; _p.grid.y = Math.floor(_p.y / data.gridSize); if(_p.grid.y >= gridRows) _p.grid.y = gridRows-1; if(_p.grid.y < 0) _p.grid.y = 0; if(_p.grid.y !== _oldGridY){ delete particleGrid[_p.grid.x][_oldGridY][_p.id]; particleGrid[_p.grid.x][_p.grid.y][_p.id] = _p; } }; // convenience functions for adding a value to X and Y _p.addX = function addX(x){ _p.setX(_p.x + x); }; _p.addY = function addY(y){ _p.setY(_p.y + y); }; // start out attracting (not repelling) other particles _p.exploding = 0; // setup the initial value to track how many other // particles this particle is currently attracting. // This will be set in our update method. _p.attracting = 0; // this will serve as a temporary holding bin for // storing the attracting value for the next update. // this is because we don't know how many are being // attracted by this one until the update finishes. _p.attractingNext = 0; // We want each particle to adjust its color based on how many // other particles it is attracting, this sets the start value _p.hue = data.startingHue; _p.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. _p.attractionRadius = data.gridSize/2; // the radius of this particle _p.radius = data.gridSize/8; // start this particle with no velocity _p.velx = Math.random() * 2 - 1; _p.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 _p = this; var _radiusFrac = _p.radius/_p.attractionRadius; var _af = _p.exploding ? _p.exploding/explodeTime : _p.af; var _hue = _p.exploding ? _af*data.hueRange-data.startingHue : _p.hue; var _motionx = (this.velx < 0) ? this.velx * -1 : this.velx; var _motiony = (this.vely < 0) ? this.vely * -1 : this.vely; var _motion = (_motionx + _motiony) / 2; //var _hue = _af*data.hueRange-data.startingHue; var _gradient = ctx.createRadialGradient(_p.x, _p.y, 1, _p.x, _p.y, _p.attractionRadius); _gradient.addColorStop(0, "hsla(" + _hue + ",100%,50%," + (_motion/5+0.06) + ")"); _gradient.addColorStop(_radiusFrac, "hsla(" + _hue + ",100%,50%,0)"); _gradient.addColorStop(_radiusFrac+0.05, "hsla(" + _hue + ",100%,50%," + (_motion/10+0.06) + ")"); _gradient.addColorStop(_radiusFrac+0.06, "hsla(" + _hue + ",100%,50%,0)"); _gradient.addColorStop(0.8, "hsla(" + _hue + ",100%,50%,0)"); _gradient.addColorStop(1, "hsla(" + _hue + ",100%,80%," + (_af*0.02) + ")"); ctx.fillStyle = _gradient; ctx.beginPath(); ctx.arc(_p.x, _p.y, _p.attractionRadius, 0, Math.PI * 2, false); ctx.fill(); }; Particle.prototype.update = function(){ var _p = this, _pG = particleGrid, _col, _row, _id, _colStart = (_p.x%data.gridSize > data.gridSize/2 ? 0 : -1) + this.grid.x, _colEnd = _colStart + 1, _rowStart = (_p.y%data.gridSize > data.gridSize/2 ? 0 : -1) + this.grid.y, _rowEnd = _rowStart + 1; for(_col=_colStart; _col<=_colEnd; _col++){ if(!_pG[_col]) continue; for(_row=_rowStart; _row<=_rowEnd; _row++){ if(!_pG[_col][_row]) continue; var _cell = _pG[_col][_row]; for(_id in _cell){ var _p2 = _cell[_id]; if(_p2.id > _p.id){ applyForces(_p, _p2); } } } } // 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(_p.x + _p.radius >= winWidth && _p.velx > 0){ _p.velx *= -0.7; } else if(_p.x - _p.radius < 0 && _p.velx < 0) { _p.velx *= -0.7; } if(_p.y + _p.radius > winHeight && _p.vely > 0){ _p.vely *= -0.7; } else if(_p.y - _p.radius < 0 && _p.vely < 0) { _p.vely *= -0.7; } if(mouseIsDown){ var _mdx = _p.x - mX; var _mdy = _p.y - mY; _mdist = Math.sqrt(_mdx*_mdx + _mdy*_mdy); var _maxDist = data.gridSize*5; if(_mdist < _maxDist){ var _af = (_maxDist-_mdist)/_maxDist, _modx = (_mdx * _p.velx < 0) ? 0.7 : 1, _mody = (_mdy * _p.vely < 0) ? 0.7 : 1; _p.velx -= ((_mdx*_af)/200) * _modx; _p.vely -= ((_mdy*_af)/200) * _modx; } } // We now know all the particles that p1 is attracting // so we can set that value for use on the next update _p.attracting = _p.attractingNext; // and clear it out so it's ready to use next time. _p.attractingNext = 0; // what fraction of the overall particles are we attracting? // I call it the attraction fraction (af) _p.af = (_p.attracting + 1) / totalParticles / data.explodeThreshold; _p.af = Math.round(_p.af * 100) / 100; if(!_p.exploding && _p.af >= 1){ _p.exploding = explodeTime; } // set this particle's hue based on how many other // particles it is attracting. _p.hue = data.startingHue + _p.af * data.hueRange; }; // ------------------------------ // // #### Draw the canvas data #### // // ------------------------------ // function drawScene(){ if(!initializing){ ctx.clearRect(0, 0, winWidth, winHeight); // draw each particle for(var i=0; i
99, // add velocity if the particle has almost stopped _slow = _p.velx < 0.2 && _p.velx > -0.2 && _p.vely < 0.2 && _p.vely > -0.2; if((_slow || _flinch) && !_p.exploding){ // add random amount of velocity between -3 and 3 _p.velx += Math.random() * 2 - 1 + _p.velx; _p.vely += Math.random() * 2 - 1 + _p.vely; } // apply velocity to particle _p.addX( _p.velx * tick ); _p.addY( _p.vely * tick ); // take time off the explosion timer if we're exploding right now if(_p.exploding) _p.exploding--; } //console.log(' => particleGrid', particleGrid); // REMOVE THIS LINE (USED IN TESTING ONLY) for(i=0; i < particles.length; i++){ particles[i].update(); } } function applyForces(_p1, _p2){ // 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 var _attractAmt = 0; if(_distance < data.gridSize) { _attractAmt += (data.gridSize - _distance) / data.gridSize; _p1.attractingNext++; _p2.attractingNext++; // particles touching an exploding particle should also be exploding if(_p2.exploding >= explodeTime-1){ _p1.exploding = explodeTime; } var _boom = (_p2.exploding || _p1.exploding) ? -2 : 1, // mod increaces the attraction if the 2 particles // are headed away from each other to encourage grouping _modx = (_dx * _p2.velx < 0) ? 0.7 : 1, _mody = (_dy * _p2.vely < 0) ? 0.7 : 1; // adjust for how fast our FPS clock is moving _attractAmt *= tick; // _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/1000; var _vy = _dy*_attractAmt/1000; _p1.velx += _vx * _modx * _boom; _p1.vely += _vy * _mody * _boom; _p2.velx -= _vx * _modx * _boom; _p2.vely -= _vy * _mody * _boom; } } // -------------------------------------------- // // #### Initialize and setup the animation #### // // -------------------------------------------- // function init(){ var i, ii; initializing = true; // Get a reference to our canvas canvas = document.getElementById("canvas") || document.createElement("canvas"); canvas.id = 'canvas'; // add the canvas to the document document.body.appendChild(canvas); sizeCanvas(); // setup init values particles = []; particleGrid = []; // calculate totalParticles based on the size of the canvas totalParticles = Math.round(winHeight/(data.gridSize*2)) * Math.round(winWidth/(data.gridSize*2)) * data.particleDensity; gridCols = Math.ceil(winWidth/data.gridSize); gridRows = Math.ceil(winHeight/data.gridSize); particleGrid = new Array(gridCols); i = particleGrid.length; for(; i-- ;){ particleGrid[i] = new Array(gridRows); ii = particleGrid[i].length; for(; ii-- ;){ particleGrid[i][ii] = {}; } } // Get the 2d drawing surface of the canvas ctx = canvas.getContext("2d"); // create all of our particles and push them into our main array to hold. i = totalParticles; for(; i-- ;){ var _p = new Particle(i); particles.push(_p); particleGrid[_p.grid.x][_p.grid.y][_p.id] = _p; } if(!initialized){ drawScene(); bindEvents(); initialized = true; } initializing = false; } function sizeCanvas(){ 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); // resize canvas when window resizes window.addEventListener('resize', init, false); } // Initialize the animation init(); return data; })(); var gui = new dat.GUI(); gui.add(firecell, 'startingHue').min(0).max(320).step(1); gui.add(firecell, 'hueRange').min(-320).max(320).step(1); gui.add(firecell, 'explodeThreshold').min(0.1).max(0.8).step(0.05); gui.add(firecell, 'particleDensity').min(1).max(50).step(1).onChange(firecell.init); gui.add(firecell, 'gridSize').min(20).max(400).step(10).onChange(firecell.init); gui.add(firecell, 'drawGrid'); gui.close();
CSS
body { padding: 0; margin: 0; overflow: hidden; background: #06072d; /* Old browsers */ background: -moz-linear-gradient(top, #06072d 0%, #000000 100%); /* FF3.6+ */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#06072d), color-stop(100%,#000000)); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, #06072d 0%,#000000 100%); /* Chrome10+,Safari5.1+ */ background: -o-linear-gradient(top, #06072d 0%,#000000 100%); /* Opera 11.10+ */ background: -ms-linear-gradient(top, #06072d 0%,#000000 100%); /* IE10+ */ background: linear-gradient(to bottom, #06072d 0%,#000000 100%); /* W3C */ }
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