JAVASCRIPT
/*
* fluid JS
* ge1doot - 1 May 2011
* --------------------
* adapted from http://violentcoding.com/blog/2008/07/26/archivhtml {
overflow: hidden;
touch-action: none;
content-zooming: none;
}
body {
position: absolute;
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: #222;
}
canvas {
position: absolute;
background: #000;
width: 100%;
height: 100%;
}es/135 (dead link)
* ref: http://www.cs.ubc.ca/~rbridson/fluidsimulation/
*/
~ function() {
'use strict';
var pmX = 0,
pmY = 0,
nbX, nbY, grid = [],
particles = [];
////////////////
var res = 20,
nbP = 1000,
psi = 50;
////////////////
// ----- Particle -----
var Particle = function(x, y) {
this.x = this.px = x;
this.y = this.py = y;
this.xvel = 0;
this.yvel = 0;
this.next = true;
};
Particle.prototype.update = function() {
if (this.x > 0 && this.x < canvas.width && this.y > 0 && this.y < canvas.height) {
var x = (0.5 + this.x / res) | 0;
var y = (0.5 + this.y / res) | 0;
if (x >= nbX) x = nbX - 1;
if (y >= nbY) y = nbY - 1;
// ----- apply grid -----
var cell = grid[y * nbX + x];
var ax = (this.x % res) / res;
var ay = (this.y % res) / res;
this.xvel += (1 - ax) * cell.xvel * 0.05 + ax * cell.R.xvel * 0.05 + ay * cell.B.xvel * 0.05;
this.yvel += (1 - ay) * cell.yvel * 0.05 + ax * cell.R.yvel * 0.05 + ay * cell.B.yvel * 0.05;
this.x += this.xvel;
this.y += this.yvel - 0.5;
// ----- canvas painting -----
var speed = (0.5 + this.xvel + this.yvel) | 0;
ctx.lineWidth = speed * 0.8 + 2;
ctx.lineCap = "round";
ctx.beginPath();
ctx.moveTo((0.5 + this.x) | 0, (0.5 + this.y) | 0);
ctx.lineTo((0.5 + this.px) | 0, (1.5 + this.py) | 0);
ctx.strokeStyle = "rgba(" + (speed * 25) + "," + 255 + "," + (speed * 25) + ", 1)";
ctx.stroke();
this.px = this.x;
this.py = this.y;
} else {
// ----- new particle -----
this.x = this.px = Math.random() * canvas.width;
this.y = this.py = Math.random() * canvas.height;
this.xvel = 0;
this.yvel = 0;
}
this.xvel *= 0.5;
this.yvel *= 0.5;
// ---- fast loop ----
return this.next;
};
// ----- Grid -----
var Grid = function(x, y) {
this.x = x;
this.y = y;
this.xvel = 0;
this.yvel = 0;
this.pressure = 0;
this.L = new nullGrid();
this.B = new nullGrid();
this.T = new nullGrid();
this.R = new nullGrid();
this.TL = new nullGrid();
this.TR = new nullGrid();
this.BL = new nullGrid();
this.BR = new nullGrid();
this.next = true;
};
var nullGrid = function() {
this.x = 0;
this.y = 0;
this.xvel = 0;
this.yvel = 0;
this.pressure = 0;
};
Grid.prototype.update = function() {
//----- pointer ------
var dx = this.x - pointer.x;
var dy = this.y - pointer.y;
var dist = Math.sqrt(dy * dy + dx * dx);
if (dist < psi) {
if (dist < 8) dist = psi;
var p = psi / dist;
this.xvel += (pointer.x - pmX) * p;
this.yvel += (pointer.y - pmY) * p;
}
// ----- pressure -----
this.pressure = (
this.TL.xvel * 0.5 + this.L.xvel + this.BL.xvel * 0.5 - this.TR.xvel * 0.5 - this.R.xvel - this.BR.xvel * 0.5 +
this.TL.yvel * 0.5 + this.T.yvel + this.TR.yvel * 0.5 - this.BL.yvel * 0.5 - this.B.yvel - this.BR.yvel * 0.5
) * 0.25;
// ----- velocity -----
this.xvel += (this.TL.pressure * 0.5 + this.L.pressure + this.BL.pressure * 0.5 - this.TR.pressure * 0.5 - this.R.pressure - this.BR.pressure * 0.5) * 0.25;
this.yvel += (this.TL.pressure * 0.5 + this.T.pressure + this.TR.pressure * 0.5 - this.BL.pressure * 0.5 - this.B.pressure - this.BR.pressure * 0.5) * 0.25;
this.xvel *= 0.996;
this.yvel *= 0.996;
return this.next;
};
// ----- create grid -----
var createGrid = function() {
grid = [];
particles = [];
// ----- create grid -----
for (var y = 0; y < nbY; y++) {
for (var x = 0; x < nbX; x++) {
grid[y * nbX + x] = new Grid(x * res, y * res);
}
}
grid[grid.length - 1].next = false;
// ----- link surrounding cells -----
for (var x = 0; x < nbX; x++) {
for (var y = 0; y < nbY; y++) {
var cell = grid[y * nbX + x];
if (y > 0) {
var T = grid[(y - 1) * nbX + x];
cell.T = T;
T.B = cell;
}
if (x > 0) {
var L = grid[y * nbX + x - 1];
cell.L = L;
L.R = cell;
}
if (y > 0 && x > 0) {
var TL = grid[(y - 1) * nbX + x - 1];
cell.TL = TL;
TL.BR = cell;
}
if (y > 0 && x < nbX - 1) {
var TR = grid[(y - 1) * nbX + x + 1];
cell.TR = TR;
TR.BL = cell;
}
}
}
// ---- create particles -----
for (var i = 0; i < nbP; i++) {
particles.push(new Particle(
Math.random() * canvas.width,
Math.random() * canvas.height
));
}
particles[particles.length - 1].next = false;
}
// ---- main loop ----
var run = function() {
requestAnimationFrame(run);
ctx.fillStyle = "rgba(0,0,0,0.04)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (
var i = 0; grid[i++].update();
);
for (
var i = 0; particles[i++].update();
);
pmX = pointer.x;
pmY = pointer.y;
};
// ---- set canvas ----
var canvas = {
width: 0,
height: 0,
elem: document.createElement('canvas'),
resize: function() {
var o = this.elem;
var w = this.elem.offsetWidth * 1;
var h = this.elem.offsetHeight * 1;
if (w !== this.width || h !== this.height) {
for (this.left = 0, this.top = 0; o !== null; o = o.offsetParent) {
this.left += o.offsetLeft;
this.top += o.offsetTop;
}
this.width = this.elem.width = w;
this.height = this.elem.height = h;
nbX = Math.round(w / res);
nbY = Math.round(h / res);
createGrid();
}
},
init: function() {
var ctx = this.elem.getContext('2d');
document.body.appendChild(this.elem);
window.addEventListener('resize', this.resize.bind(this), false);
this.resize();
return ctx;
}
};
var ctx = canvas.init();
// ---- pointer ----
var pointer = (function(canvas) {
var pointer = {
x: canvas.width * 0.5,
y: canvas.height * 0.5,
canvas: canvas,
pointer: function(e) {
var touchMode = e.targetTouches,
pointer;
if (touchMode) {
e.preventDefault();
pointer = touchMode[0];
} else pointer = e;
this.x = pointer.clientX - this.canvas.left;
this.y = pointer.clientY - this.canvas.top;
},
};
window.addEventListener('mousemove', function(e) {
this.pointer(e);
}.bind(pointer), false);
window.addEventListener('touchmove', function(e) {
this.pointer(e);
}.bind(pointer), false);
return pointer;
}(canvas));
// ----- start -----
run();
}();