Skip to content

Instantly share code, notes, and snippets.

@amwmedia
Created December 17, 2014 19:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amwmedia/c038cace25a658d41966 to your computer and use it in GitHub Desktop.
Save amwmedia/c038cace25a658d41966 to your computer and use it in GitHub Desktop.
Pushing Particles

Pushing Particles

The number of particles will be increased until your CPU can no longer maintain at least 45 FPS. If your FPS drops below 45, particles will be removed. The particles will also track your mouse, so that's fun too :-)

How much can YOUR system handle?!

A Pen by Andrew Worcester on CodePen.

License.

(function(){
// cross browser requestAnimationFrame loop to handle the animation looping
var rAF = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
// average particles per 4 grid blocks
var winWidth, winHeight,
gridCols, gridRows,
startCount, particles, tracer,
canvas, ctx,
targetFPS = 60,
avgFPS = 60,
timer = (new Date()).getTime(),
lastMouseMove = (new Date()).getTime(),
frameBuffer = [60,60,60,60,60,60,60,60,60,60],
FPS = 0,
tick = 1,
fillStyles = [],
friction = 0.97,
initialized = false, initializing = true;
// ------------------------ //
// #### Particle Class #### //
// ------------------------ //
function Particle(){
// reference to the current particle
var _p = this;
// set this particle's initial location to a random
// place on the screen within the bounds of the window.
_p.x = winWidth/2;
_p.y = winHeight/2;
// the radius of this particle
_p.tracer = {
x: winWidth/2,
y: winHeight/2
};
// start this particle with velocity
_p.velx = Math.random() * 4 - 2;
_p.vely = Math.random() * 4 - 2;
}
// Setup a common draw function that will be called
// by all members of the Particle class
Particle.prototype.draw = function(){
ctx.fillRect(this.x, this.y, 2, 2);
};
Particle.prototype.update = function(tick){
var _p = this,
_vel,
_t = _p.tracer,
diffX, diffY,
iAmLeader = _p === particles[0] && timer - lastMouseMove > 5000,
jump = iAmLeader ? 20 : 8;
if (iAmLeader) {
tracer.x = _p.x;
tracer.y = _p.y;
} else {
diffX = _t.x - _p.x;
diffY = _t.y - _p.y;
}
if (Math.abs(_p.velx) + Math.abs(_p.vely) < 0.5 && (Math.abs(diffX) < 1 || Math.abs(diffY) < 1 || iAmLeader)) {
_p.tracer = {
x: tracer.x,
y: tracer.y
};
_p.velx = Math.random() * jump - (jump/2);
_p.vely = Math.random() * jump - (jump/2);
}
if (diffX && diffY) {
_p.velx += diffX / 1000;
_p.vely += diffY / 1000;
}
// friction
_p.velx *= friction;
_p.vely *= friction;
// 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 >= winWidth && _p.velx > 0 || _p.x < 0 && _p.velx < 0){ _p.velx *= -1; }
if(_p.y >= winHeight && _p.vely > 0 || _p.y < 0 && _p.vely < 0){ _p.vely *= -1; }
// tick adjust
_p.x += _p.velx * tick;
_p.y += _p.vely * tick;
_vel = Math.abs(_p.velx) + Math.abs(_p.vely);
_p.fillStyle = Math.min(Math.round((_vel/10) * 99), 99);
};
function setTracer(e) {
e = e || window.event;
tracer.x = e.pageX;
tracer.y = e.pageY;
lastMouseMove = (new Date()).getTime();
}
// ------------------------------ //
// #### Draw the canvas data #### //
// ------------------------------ //
function drawScene(){
var i, len, f, _p;
ctx.clearRect(0, 0, winWidth, winHeight);
_p = particles[i-1];
for (f = fillStyles.length; f--;) {
// draw each particle
len = particles.length;
ctx.fillStyle = fillStyles[f];
for(i = 0; i < len ; i++){
_p = particles[i];
if (_p.fillStyle !== f) { continue; }
_p.draw();
}
}
// draw the FPS clock
ctx.lineWidth=1;
ctx.fillStyle="#CCCCCC";
ctx.font="16px sans-serif";
ctx.fillText("FPS: " + Math.round(avgFPS), 20, 30);
ctx.fillText("Particles: " + particles.length, 20, 50);
// update the scene
update();
// schedule the next update
rAF(drawScene);
}
// ------------------------------------------------ //
// #### Update the scene, animate all elements #### //
// ------------------------------------------------ //
function update(){
// 1000ms is 1s, divided by the number of ms it took
// us since last update gives us how many times we
// could have performed this frame within 1 second (FPS)
var _timer = (new Date()).getTime();
var _lastFPS = FPS;
var _i;
FPS = 1000 / (_timer - timer);
frameBuffer.shift();
frameBuffer.push(FPS);
// average the new FPS value with the last one to ease the change rate
avgFPS = frameBuffer.reduce(function (prev, cur) { return prev + cur; }, 0) / frameBuffer.length;
timer = _timer;
// tick is how far from the ideal speed we are
// if we are rendering slower, this number will be higher.
// we'll use this to adjust the amount of movement we perform
// on each particle per frame.
tick = targetFPS / FPS;
if (avgFPS > 55) {
particles.push(new Particle());
particles.push(new Particle());
} else if (avgFPS < 45) {
particles.pop();
particles.pop();
}
// 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(_i = particles.length; _i-- ;){
particles[_i].update(tick);
}
}
// -------------------------------------------- //
// #### Initialize and setup the animation #### //
// -------------------------------------------- //
function init(){
var i;
initializing = true;
// Get a reference to our canvas
canvas = document.getElementById("canvas") || document.createElement("canvas");
canvas.id = 'canvas';
document.body.appendChild(canvas);
sizeCanvas();
ctx = canvas.getContext("2d");
// setup init values
particles = [];
startCount = 100;
for (i = 100; i--;) {
fillStyles[i] = 'hsla(' +
(200 * (i/99) + 200) + ', ' +
'100%, 60%, ' +
((i + 1) / 70 + 0.3) +
')';
}
// create all of our particles and push them into our main array to hold.
i = startCount;
for(; i-- ;){
particles[i] = new Particle();
}
tracer = {
x: winWidth/2,
y: winHeight/2
};
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(){
// resize canvas when window resizes
window.addEventListener('resize', init, false);
window.addEventListener('mousemove', setTracer, false);
}
// Initialize the animation
init();
})();
body {
padding: 0;
margin: 0;
overflow: hidden;
background-color: black;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment