Direct Link
Source code
| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>The Momentum Equation - Interactive DHTML demos</title> |
| <meta name="Author" content="Gerard Ferrandez at http://www.dhteumeuleu.com"> |
| <style type="text/css"> |
| body { |
| margin: 0px; |
| padding: 0px; |
| background: #111; |
| width: 100%; |
| height: 100%; |
| color:#fff; |
| font-size:12px; |
| font-family: arial,verdana; |
| } |
| #screen { |
| position: absolute; |
| left: 50%; |
| top: 50%; |
| background: #000; |
| overflow: hidden; |
| width: 800px; |
| height: 500px; |
| margin-left:-400px; |
| margin-top:-250px; |
| } |
| #screen img { |
| position: absolute; |
| } |
| </style> |
| <script type="text/javascript"> |
| // ========================================================================= |
| // ===== fluid js implementation ===== |
| // ---------------------------------------------------------------------- |
| // script: Gerard Ferrandez - 1 May 2011 |
| // http://www.dhteumeuleu.com/ |
| // use under a CC-BY-NC license |
| // ---------------------------------------------------------------------- |
| // adapted from : http://www.processing.org/learning/topics/fluid.html |
| // and from : http://violentcoding.com/blog/2008/07/26/archives/135 |
| // ref: http://www.cs.ubc.ca/~rbridson/fluidsimulation/ |
| // ========================================================================= |
| "use strict"; |
| var fluid = function () { |
| // ----- private vars ----- |
| var canvas, ctx, nw, nh, nx, ny, |
| mouseX = 0, mouseY = 0, pmX = 0, pmY = 0, nbX, nbY, res, psi, |
| nbP, grid = [], particles, cells, prev; |
| // ----- Particle prototype ----- |
| var Particle = function (x, y) { |
| this.x = this.px = x; |
| this.y = this.py = y; |
| this.xvel = 0; |
| this.yvel = 0; |
| if (!particles) particles = this; else prev.nextParticle = this; |
| prev = this; |
| }; |
| Particle.prototype.update = function () { |
| if (this.x > 0 && this.x < nw && this.y > 0 && this.y < nh) { |
| 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; |
| // ----- 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() * nw; |
| this.y = this.py = Math.random() * nh; |
| this.xvel = 0; |
| this.yvel = 0; |
| } |
| this.xvel *= 0.5; |
| this.yvel *= 0.5; |
| // ----- next particle ----- |
| this.nextParticle ? this.nextParticle.update() : false; |
| }; |
| // ----- Grid prototype ----- |
| 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(); |
| if (!cells) cells = this; else prev.nextCell = this; |
| prev = this; |
| }; |
| var nullGrid = function () { |
| this.x = 0; |
| this.y = 0; |
| this.xvel = 0; |
| this.yvel = 0; |
| this.pressure = 0; |
| }; |
| Grid.prototype.update = function (mxvel, myvel) { |
| //----- mouse ------ |
| var dx = this.x - mouseX; |
| var dy = this.y - mouseY; |
| var dist = Math.sqrt(dy * dy + dx * dx); |
| if (dist < psi) { |
| if (dist < 4) dist = psi; |
| var p = psi / dist; |
| this.xvel += mxvel * p; |
| this.yvel += myvel * 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.99; |
| this.yvel *= 0.99; |
| // ---- next cell ----- |
| this.nextCell ? this.nextCell.update(mxvel, myvel) : false; |
| }; |
| // ----- main loop ----- |
| var run = function () { |
| ctx.fillStyle = "rgba(0,0,0,0.05)"; |
| ctx.fillRect(0, 0, nw, nh); |
| cells.update(mouseX - pmX, mouseY - pmY); |
| particles.update(); |
| pmX = mouseX; |
| pmY = mouseY; |
| }; |
| // ----- initialization ----- |
| var init = function (params) { |
| res = params.resolution; |
| nbP = params.nParticles; |
| psi = params.penSize; |
| var d = canvas = document.getElementById("screen"); |
| ctx = canvas.getContext("2d"); |
| // ---- dimensions ---- |
| nw = d.offsetWidth; |
| nh = d.offsetHeight; |
| canvas.width = nw; |
| canvas.height = nh; |
| for (nx = 0, ny = 0; d != null; d = d.offsetParent) { |
| nx += d.offsetLeft; |
| ny += d.offsetTop; |
| } |
| nbX = nw / res; |
| nbY = nh / res; |
| // ----- 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); |
| } |
| } |
| // ----- 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++) { |
| new Particle( |
| Math.random() * nw, |
| Math.random() * nh |
| ); |
| } |
| // ----- mouse events ----- |
| document.addEventListener('mousemove', function (e) { |
| mouseX = e.clientX - nx; |
| mouseY = e.clientY - ny; |
| }, false); |
| canvas.addEventListener('click', function (e) { |
| pmX = mouseX; |
| pmY = mouseY + 100; |
| }, false); |
| // ----- touch event ----- |
| document.addEventListener('touchmove', function(e) { |
| e.preventDefault(); |
| var touch = e.touches[0]; |
| mouseX = touch.pageX - nx; |
| mouseY = touch.pageY - ny; |
| }, false); |
| // ----- start engine ----- |
| setInterval(run, 16); |
| }; |
| return { |
| //////////////////////////////////////////////////////////////////////////// |
| /* ==== public functions ==== */ |
| init : function (params) { |
| window.addEventListener('load', function () { |
| init(params); |
| }, false); |
| } |
| } |
| }(); |
| //////////////////////////////////////////////////////////////////////////////// |
| // ---- start script ---- |
| fluid.init({ |
| nParticles: 500, |
| resolution: 20, |
| penSize: 80 |
| }); |
| // ---------------------- |
| // |
| </script> |
| </head> |
| <body> |
| <canvas id="screen">Sorry, this demo requires a web browser which supports HTML5 canvas!</canvas> |
| </body> |
| </html> |


Recent Comments
January 9, 2012 (12:12)
November 23, 2011 (10:25)
November 4, 2011 (12:52)