cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              	<p id="fps"></p>
	<article>
		change mouse actions with dat-gui<br/>
		to update, delete or move click on fountain
	</article>
	<canvas id="mainCanvas"></canvas>
            
          
!
            
              body {
  margin: 0;
  padding: 0;
  background: #241C1F;
}
canvas {
  margin: 0 auto;
  display: block;
  border-left: 0;
  border-right: 0;
  background: #241C1F;
}
p {
  position: absolute;
  margin: 10px;
  color: #D9E01B;
}
article {
  position: absolute;
  margin: 35px 10px;
  color: #ffffff;
}
            
          
!
            
              // https://twitter.com/msvaljek
// https://msvaljek.blogspot.com/2013/08/diy-canvas-fountain.html

// standard shim
window.requestAnimFrame = (function(){
	return window.requestAnimationFrame ||
	window.webkitRequestAnimationFrame ||
	window.mozRequestAnimationFrame ||
	function( callback ) {
		window.setTimeout(callback, 1000 / 60);
	};
})();
function distance(x1, y1, x2, y2) {
	return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
}
function randomMax(max) {
	return Math.floor(Math.random() * max);
}
function getRandomArbitary (min, max) {
    return min + Math.random() * (max - min);
}

function findAndSelectFountain(x, y) {
	for (var i = 0; i < fountains.length; i++) {
		fountains[i].sel = false;
	}
	for (i = 0; i < fountains.length; i++) {
		var fnt = fountains[i];
		if (distance(fnt.x, fnt.y, x, y) <= fountainProps.radius) {
			fnt.sel = true;
			activeFountain = fnt;
			return i;
		}
	}
}

function deselectActiveFountain() {
	if (activeFountain) {
		activeFountain.sel = false;
	}
	activeFountain = undefined;
}
function mouseClick(event) {
    var mouseX = event.clientX - document.documentElement.scrollLeft - canvas.offsetLeft;
    var mouseY = event.clientY - document.documentElement.scrollTop - canvas.offsetTop;

    deselectActiveFountain();

    if (globals.operation === 'Add Fountain') {
		fountains.push(new Fountain(mouseX, mouseY));
    } else if (globals.operation === 'Update Fountain') {
		findAndSelectFountain(mouseX, mouseY);
		updateGlobalsBySelectedFountain();
    } else if (globals.operation === 'Delete Fountain') {
		var f = findAndSelectFountain(mouseX, mouseY);
		if (f !== undefined) {
			fountains.splice(f, 1);
		}
    } else if (globals.operation === 'Move Fountain') {
		moveState = !moveState;
		if (!moveState) {
			findAndSelectFountain(mouseX, mouseY);
		}
    }
}
function mouseMove(event) {
    var mouseX = event.clientX - document.documentElement.scrollLeft - canvas.offsetLeft;
    var mouseY = event.clientY - document.documentElement.scrollTop - canvas.offsetTop;

	if (activeFountain && globals.operation === 'Move Fountain') {
		activeFountain.x = mouseX;
		activeFountain.y = mouseY;
	}

}

// dom stuff
var canvas = document.getElementById('mainCanvas');
var fpsOut = document.getElementById('fps');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var ctx = canvas.getContext('2d');
var fps = 0, now, lastUpdate = (new Date())*1 - 1, fpsFilter = 50;

var fountains = [];
var particles = [];
var particleSplice = [];

var activeFountain;
var moveState = true;

var globals = {
	operation : 'Add Fountain'
};
var fountainProps = {
	selectedColor : '#b61c51',
	color : '#769b7c',
	radius : 5,
	gravity : 0.1,

	particleColor : 'rgb(88, 160, 237)',
	speed : 10,
	cycle : 5,
	angle : Math.PI / 4,

	devMin: 0.90
};

var Particle = function (x, y, angle, speed, color) {
	this.x = x;
	this.y = y;
	this.color = color;
	this.r = 1 + randomMax(2);

	this.dx = speed * Math.cos(angle - Math.PI / 2);
	this.dy = speed * Math.sin(angle - Math.PI / 2);
};
Particle.prototype.draw = function (id) {
	this.dy += fountainProps.gravity;
	this.x += this.dx;
	this.y += this.dy;
	ctx.fillStyle = this.color;
	ctx.beginPath();
	ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
	ctx.fill();

	if (this.x < 0 || this.x > canvas.width || this.y > canvas.height) {
		particleSplice.push(id);
	}
};

function updateSelectedFountain() {
	if (activeFountain) {
		activeFountain.particleColor = fountainProps.particleColor;
		activeFountain.cycle = fountainProps.cycle;
		activeFountain.angle = fountainProps.angle;
		activeFountain.speed = fountainProps.speed;

		activeFountain.cycleCounter = activeFountain.cycle;
	}
}

function updateGlobalsBySelectedFountain() {
	if (activeFountain) {
		fountainProps.particleColor = activeFountain.particleColor;
		fountainProps.cycle = activeFountain.cycle;
		fountainProps.angle = activeFountain.angle;
		fountainProps.speed = activeFountain.speed;
	}
}

var Fountain = function (x, y) {
	this.x = x;
	this.y = y;
	this.sel = false;

	this.particleColor = fountainProps.particleColor;
	this.cycle = fountainProps.cycle;
	this.angle = fountainProps.angle;
	this.speed = fountainProps.speed;

	this.cycleCounter = this.cycle;
};
Fountain.prototype.draw = function () {
	this.cycleCounter--;

	if (this.cycleCounter <= 0) {
		this.cycleCounter = this.cycle;
		particles.push(new Particle(this.x, this.y, this.angle, getRandomArbitary(fountainProps.devMin, 1) * this.speed, this.particleColor));
	}

	ctx.beginPath();
	ctx.fillStyle = this.sel ? fountainProps.selectedColor : fountainProps.color;
	ctx.arc(this.x, this.y, fountainProps.radius, 0, 2 * Math.PI);
	ctx.closePath();
	ctx.fill();
};

canvas.addEventListener.apply(canvas, ['mousedown', mouseClick, false]);
canvas.addEventListener.apply(canvas, ['mousemove', mouseMove, false]);

var gui = new dat.GUI();

var f1 = gui.addFolder('Globals');
f1.addColor(fountainProps, 'color').name('Source color');
f1.addColor(fountainProps, 'selectedColor').name('Selected color');
f1.add(fountainProps, 'devMin').min(0.1).max(1).step(0.05).name('Speed deviation');
f1.add(fountainProps, 'gravity').min(0.01).max(2).step(0.01).name('Gravity').onFinishChange(updateSelectedFountain);
f1.open();

var f2 = gui.addFolder('Specific (select or add fountain)');
f2.addColor(fountainProps, 'particleColor').name('Particle Color').listen().onChange(updateSelectedFountain);
f2.add(fountainProps, 'cycle').min(1).max(50).step(1).name('Cycle').listen().onFinishChange(updateSelectedFountain);
f2.add(fountainProps, 'angle').min(-Math.PI).max(Math.PI).step(0.001).name('Angle').listen().onFinishChange(updateSelectedFountain);
f2.add(fountainProps, 'speed').min(0).max(20).step(1).name('Speed').listen().onFinishChange(updateSelectedFountain);
f2.open();

var f3 = gui.addFolder('Mouse Click Action');
f3.add(globals, 'operation', [ 'Add Fountain', 'Update Fountain', 'Delete Fountain', 'Move Fountain'] ).name('Action');
f3.open();

window.onresize = function() {
	canvas.width = window.innerWidth;
	canvas.height = window.innerHeight;
};

// just some initial fountains
fountains.push(new Fountain(canvas.width * 0.1, 100));
fountains.push(new Fountain(canvas.width * 0.1, 200));
fountains.push(new Fountain(canvas.width * 0.1, 300));

(function animloop(){
	requestAnimFrame(animloop);
	ctx.clearRect(0, 0, canvas.width, canvas.height);

	for (var f = 0; f < fountains.length; f++) {
		fountains[f].draw();
	}
	for (var h = 0; h < particles.length; h++) {
		particles[h].draw(h);
	}
	particleSplice.sort(function(a,b){return a - b;});
	for (var a = 0; a < particleSplice.length; a++) {
		particleSplice[a] += a;
	}
	for (a = 0; a < particleSplice.length; a++) {
		particles.splice(particleSplice[a], 1);
	}
	particleSplice.length = 0;

	var thisFrameFPS = 1000 / ((now=new Date()) - lastUpdate);
	fps += (thisFrameFPS - fps) / fpsFilter;
	lastUpdate = now;
})();

setInterval(function(){
  fpsOut.innerHTML = fps.toFixed(1) + " fps";
}, 1000);
            
          
!
999px
Loading ..................

Console