Skip to content

Instantly share code, notes, and snippets.

@monde
Created February 8, 2012 20:21
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save monde/1773107 to your computer and use it in GitHub Desktop.
Save monde/1773107 to your computer and use it in GitHub Desktop.
codes to Nick Kwiatek's ascii js animation page (http://nkwiatek.com/)
// this is in the process of being rewritten and optimized
/**
* Copyright (c) 2011 Nick Kwiatek <http://nkwiatek.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
(function(window) {
var FluidDisplayASCII = function(fluid) {
var _Config = {
// strength of forces
density: 260
// ascii gradient
//gradient: [' ', '·','.','•','¤','x','X','N','M'],
//gradient: [' ', '·','.','¤','x','X','N','M','¶'],
//gradient: [' ', '·','~','¢','»','¤','X','¥','¶'],
, gradient: [' ', '·','~','¢','c','»','¤','X','M','¶']
// animation update timing in milliseconds
, tickrate: 15
, blastDensity: 9000
, blastVelocity: 6000
};
this.Config = _Config;
// creates/retrieves stored object which allows us to manipulate the ASCII text block being displayed
var getDisplay = (function() {
// cache of ascii gradient data
var _gradient = _Config.gradient;
var _gradient_len = _Config.gradient.length - 1;
// container for holding the display text
var _ascii = [];
// the artifical dimensions of the field
var _width = null;
var _height = null;
var _length = null;
// object returned by func, for manipulating display text
var _Display = {
length: function() {
return _length;
},
// TODO: figure out how this should work in conjunction with field.reset()
reset: function() {
resizeDisplay(dimensions.width, dimensions.height, true);
},
set: function(field) {
var rowSize = field.rowSize();
var dens = field.rawDensityArray;
var y = _height - 1; do {
// small optimisations here
var yAsciiOffset = y * _width;
var yDensityOffset = (y + 1) * rowSize;
var x = _width; do {
// we use x - 1 because our array is index 1, theirs is index 0
var value = dens[x + yDensityOffset] + 0.5 | 0; // bitwise round
if (value > _gradient_len) {
value = _gradient_len;
}
_ascii[x + yAsciiOffset] = _gradient[value];
} while(x-- > 1);
} while(y--);
},
toString: function() {
// much faster implementation, by like 1000%, even on engines natively supporting Array.reduce()
// slices array by row length, appends newline
var retval = '';
for (var i = 1; i <= _height; i++) {
var range = i * _width + 1; // + 1 because the slice end is not inclusive (but we need it to be)
retval += _ascii.slice(range - _width, range).join('') + '\n';
}
return retval;
}
};
// private function for resizing private ascii container
var _resizeDisplay = function (width, height, overwrite) {
// is this how you do default values?
if (overwrite === undefined) {
overwrite = false;
}
_width = width;
_height = height;
_length = width * height;
for (var i = 1; i <= _length; i++) {
if (overwrite || _ascii[i] === undefined) {
_ascii[i] = _Config.gradient[1];
}
}
};
// Display factory (returns cache if valid)
return function(width, height) {
// if for some reason this is unoptimal,
// we can use an event listener callback on fluid resize,
// instead of requiring width/height on get()
if (width !== _Display.width && height !== _Display.height) {
_resizeDisplay(width, height);
}
return _Display;
};
})();
//
// TODO: addForce, generalize the mouse calculation operation into a func
// onclick: force
// take setUI on force obj out of display renderer
// store the bound DOM element inside this object's scope
this.bindElement = function(DOMElement) {
// used on field.update to determine whether we should apply velocity
var checkMouseMovement = false;
var checkMouseClick = false;
// mouse coordinates relative to element's origin
var mouse_cur = {x: null, y: null};
var mouse_prev = null;
var origin = (function(el) {
var x = 0, y = 0;
do {
x += el.offsetLeft;
y += el.offsetTop;
} while (el = el.offsetParent);
return { x: x, y: y };
})(DOMElement);
DOMElement.unselectable = true;
DOMElement.onmousedown = (function() {
checkMouseClick = true;
})
DOMElement.onmousemove = (function() {
var doc_body = window.document.body;
var doc_element = window.document.documentElement;
return function(e) {
// the following browser-wide mouse coordinates logic taken mostly from quirksmode
// http://www.quirksmode.org/js/events_properties.html#position
if (!e) {
e = window.event;
}
if (e.pageX || e.pageY) {
mouse_cur.x = e.pageX;
mouse_cur.y = e.pageY;
}
else if (e.clientX || e.clientY) {
mouse_cur.x = e.clientX + doc_body.scrollLeft + doc_element.scrollLeft;
mouse_cur.y = e.clientY + doc_body.scrollTop + doc_element.scrollTop;
}
// has to be initialized somewhere; better here than in the UI callback
// could probably do a crazy trick involving a named var for 'onmousemove =',
// having it first be a check for mouse_prev, then replacing the named func
// with a func that doesn't have the check, if this line of code is REALLY an issue
// probably not a factor here, but an interesting idea for somewhere more critical
if (mouse_prev === null) {
mouse_prev = {x: mouse_cur.x, y: mouse_cur.y};
}
checkMouseMovement = true;
}
})();
// called on fluid's update() method (before physics calculations)
// we can make changes to the field here
fluid.setUICallback(function(field) {
// click explosion
if (checkMouseClick) {
var x_fluid
, y_fluid
, deWidth = DOMElement.offsetWidth
, deHeight = DOMElement.offsetHeight
, fWidth = field.width()
, fHeight = field.height()
// map element coords to fluid coords
x_fluid = (mouse_cur.x * (fWidth / deWidth)) + 0.5 | 0 // bitwise round
y_fluid = (mouse_cur.y * (fHeight / deHeight)) + 0.5 | 0 // bitwise round
// apply the shockwave by setting params in the 8 pixels around the current position
// (as well as some extra dead weight in the current position)
for (var w = -1; w <= 1; w++) {
for (var h = -1; h <= 1; h++) {
field.setDensity(x_fluid + w, y_fluid + h, _Config.blastDensity)
field.setVelocity(x_fluid + w, y_fluid + h, _Config.blastVelocity * w, _Config.blastVelocity * h)
}
}
checkMouseClick = false
}
// add velocity if the mouse has moved within the body of the display
if (checkMouseMovement /*&& mouse_prev != null*/) {
var diff_x = mouse_cur.x - mouse_prev.x;
var diff_y = mouse_cur.y - mouse_prev.y;
var distance = Math.sqrt(diff_x * diff_x + diff_y * diff_y) + 0.5 | 0; // bitwise round
distance = distance < 1 ? 1 : distance;
// small optimization
var fSetVelocity = field.setVelocity;
var fSetDensity = field.setDensity;
var fWidth = field.width();
var fHeight = field.height();
var deWidth = DOMElement.offsetWidth;
var deHeight = DOMElement.offsetHeight;
var mp_x = mouse_prev.x;
var mp_y = mouse_prev.y;
// interpolate steps between the current position and the previous capture
var i = distance - 1; do {
var x = (((mp_x + diff_x * (i / distance)) / deWidth) * fWidth) + 0.5 | 0; // bitwise round
var y = (((mp_y + diff_y * (i / distance)) / deHeight) * fHeight) + 0.5 | 0; // bitwise round
fSetVelocity(x, y, diff_x, diff_y);
fSetDensity(x, y, _Config.density);
} while(i--);
// update record of calculation
mouse_prev.x = mouse_cur.x;
mouse_prev.y = mouse_cur.y;
checkMouseMovement = false;
}
});
// called at the end of every frame, after calculations are complete
// draw changes here
fluid.setDisplayFunction(function(field) {
// update display with data from field
var display = getDisplay(field.width(), field.height());
display.set(field);
// update the DOM DOMElement with the display text
/*while(DOMElement.childNodes.length >= 1) {
DOMElement.removeChild(DOMElement.firstChild);
}
DOMElement.appendChild(DOMElement.ownerDocument.createTextNode( display.toString() ));*/
DOMElement.innerHTML = display.toString();
});
};
// public interface for starting/stopping animation
this.Animation = {
interval: null,
start: function(fnOnFrameUpdate) {
if (this.interval !== null) {
return;
}
// this IIFE will return either:
// - a function that calls the callback
// - a function that just does the tick
// depending on whether fnOnFrameUpdate was passed
// todo: replace `fluid` with `callback` passed with `Function.bind(fluid, fluid.update)`
;(function(interval, tickrate, fluid, fnOnFrameUpdate) {
var fn = (fnOnFrameUpdate instanceof Function) ?
function tickWithCallback() {
fluid.update();
fnOnFrameUpdate();
interval = setTimeout(tickWithCallback, tickrate);
}
: function tickNoCallback() {
fluid.update();
interval = setTimeout(tickNoCallback, tickrate);
}
// start the clock
fn();
// redundant vars passed here to whittle down the closure nesting
// (for performance -- this shit gets called every frame)
}(this.interval, _Config.tickrate, fluid, fnOnFrameUpdate))
},
/*start: function(cbOnFrameUpdate) {
if (this.interval === null) {
var _proc = ((function(interval, tickrate) {
return function tick() {
fluid.update();
if (cbOnFrameUpdate instanceof Function) {
cbOnFrameUpdate();
}
interval = setTimeout(procClosureHandle, tickrate);
};
})(this.interval, _Config.tickrate));
procClosureHandle = _proc;
procClosureHandle();
}
},*/
stop: function() {
if (this.interval !== null) {
clearInterval(this.interval);
this.interval = null;
}
}
};
};
window['FluidDisplayASCII'] = FluidDisplayASCII;
})(window);
// Based on http://www.dgp.toronto.edu/people/stam/reality/Research/pdf/GDC03.pdf
// this is in the process of being rewritten and optimized
/**
* Copyright (c) 2009 Oliver Hunt <http://nerget.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
function FluidField() {
function addFields(x, s, dt) {
var i = size - 1; do {
x[i] += dt * s[i]
} while (i--);
}
// second slowest function
function set_bnd(b, x) {
// small caching optimizations
var lastRow = height * rowSize;
var maxEdge = (height + 1) * rowSize;
var maxWidth = (width + 1);
if (b===1) {
for (var i = 1; i <= width; i++) {
x[i] = x[i + rowSize];
x[i + maxEdge] = x[i + lastRow];
}
for (var j = 1; i <= height; i++) {
var jRow = j * rowSize;
x[jRow] = -x[1 + jRow];
x[maxWidth + jRow] = -x[width + jRow];
}
} else if (b === 2) {
for (var i = 1; i <= width; i++) {
x[i] = -x[i + rowSize];
x[i + maxEdge] = -x[i + lastRow];
}
for (var j = 1; j <= height; j++) {
var jRow = j * rowSize;
x[jRow] = x[1 + jRow];
x[maxWidth + jRow] = x[width + jRow];
}
} else {
for (var i = 1; i <= width; i++) {
x[i] = x[i + rowSize];
x[i + maxEdge] = x[i + lastRow];
}
for (var j = 1; j <= height; j++) {
var jRow = j * rowSize;
x[jRow] = x[1 + jRow];
x[maxWidth + jRow] = x[width + jRow];
}
}
x[0] = 0.5 * (x[1] + x[rowSize]);
x[maxEdge] = 0.5 * (x[1 + maxEdge] + x[lastRow]);
x[maxWidth] = 0.5 * (x[width] + x[maxWidth + rowSize]);
x[maxWidth+maxEdge] = 0.5 * (x[width + maxEdge] + x[maxWidth + lastRow]);
}
// this function is INCREDIBLY slow
function lin_solve(b, x, x0, a, c) {
if (a === 0 && c === 1) {
for (var j=1 ; j<=height; j++) {
var currentRow = j * rowSize;
++currentRow;
var i = width - 1; do {
x[currentRow] = x0[currentRow];
++currentRow;
} while (i--);
}
set_bnd(b, x);
} else {
var invC = 1 / c;
for (var k=0 ; k<iterations; k++) {
for (var j=1 ; j<=height; j++) {
var lastRow = (j - 1) * rowSize;
var currentRow = j * rowSize;
var nextRow = (j + 1) * rowSize;
var lastX = x[currentRow];
++currentRow;
var i = width; do {
lastX = x[currentRow] = (x0[currentRow] + a*(lastX+x[++currentRow]+x[++lastRow]+x[++nextRow])) * invC;
} while (i-- > 1)
}
set_bnd(b, x);
}
}
}
function diffuse(b, x, x0, dt)
{
var a = 0;
lin_solve(b, x, x0, a, 1 + 4*a);
}
function lin_solve2(x, x0, y, y0, a, c)
{
if (a === 0 && c === 1) {
for (var j=1 ; j <= height; j++) {
var currentRow = j * rowSize;
++currentRow;
for (var i = 0; i < width; i++) {
x[currentRow] = x0[currentRow];
y[currentRow] = y0[currentRow];
++currentRow;
}
}
set_bnd(1, x);
set_bnd(2, y);
} else {
var invC = 1/c;
for (var k=0 ; k<iterations; k++) {
for (var j=1 ; j <= height; j++) {
var lastRow = (j - 1) * rowSize;
var currentRow = j * rowSize;
var nextRow = (j + 1) * rowSize;
var lastX = x[currentRow];
var lastY = y[currentRow];
++currentRow;
for (var i = 1; i <= width; i++) {
lastX = x[currentRow] = (x0[currentRow] + a * (lastX + x[currentRow] + x[lastRow] + x[nextRow])) * invC;
lastY = y[currentRow] = (y0[currentRow] + a * (lastY + y[++currentRow] + y[++lastRow] + y[++nextRow])) * invC;
}
}
set_bnd(1, x);
set_bnd(2, y);
}
}
}
function diffuse2(x, x0, y, y0, dt)
{
var a = 0;
lin_solve2(x, x0, y, y0, a, 1 + 4 * a);
}
function advect(b, d, d0, u, v, dt)
{
var Wdt0 = dt * width;
var Hdt0 = dt * height;
var Wp5 = width + 0.5;
var Hp5 = height + 0.5;
for (var j = 1; j<= height; j++) {
var pos = j * rowSize;
for (var i = 1; i <= width; i++) {
var x = i - Wdt0 * u[++pos];
var y = j - Hdt0 * v[pos];
if (x < 0.5)
x = 0.5;
else if (x > Wp5)
x = Wp5;
var i0 = x | 0;
var i1 = i0 + 1;
if (y < 0.5)
y = 0.5;
else if (y > Hp5)
y = Hp5;
var j0 = y | 0;
var j1 = j0 + 1;
var s1 = x - i0;
var s0 = 1 - s1;
var t1 = y - j0;
var t0 = 1 - t1;
var row1 = j0 * rowSize;
var row2 = j1 * rowSize;
d[pos] = s0 * (t0 * d0[i0 + row1] + t1 * d0[i0 + row2]) + s1 * (t0 * d0[i1 + row1] + t1 * d0[i1 + row2]);
}
}
set_bnd(b, d);
}
function project(u, v, p, div) {
var h = -0.5 / Math.sqrt(width * height);
for (var j = 1 ; j <= height; j++ ) {
var row = j * rowSize;
var previousRow = (j - 1) * rowSize;
var prevValue = row - 1;
var currentRow = row;
var nextValue = row + 1;
var nextRow = (j + 1) * rowSize;
for (var i = 1; i <= width; i++ ) {
div[++currentRow] = h * (u[++nextValue] - u[++prevValue] + v[++nextRow] - v[++previousRow]);
p[currentRow] = 0;
}
}
set_bnd(0, div);
set_bnd(0, p);
lin_solve(0, p, div, 1, 4 );
var wScale = 0.5 * width;
var hScale = 0.5 * height;
for (var j = 1; j<= height; j++ ) {
var prevPos = j * rowSize - 1;
var currentPos = j * rowSize;
var nextPos = j * rowSize + 1;
var prevRow = (j - 1) * rowSize;
var nextRow = (j + 1) * rowSize;
for (var i = 1; i<= width; i++) {
u[++currentPos] -= wScale * (p[++nextPos] - p[++prevPos]);
v[currentPos] -= hScale * (p[++nextRow] - p[++prevRow]);
}
}
set_bnd(1, u);
set_bnd(2, v);
}
function dens_step(x, x0, u, v, dt)
{
addFields(x, x0, dt);
diffuse(0, x0, x, dt );
advect(0, x, x0, u, v, dt );
}
function vel_step(u, v, u0, v0, dt)
{
addFields(u, u0, dt );
addFields(v, v0, dt );
var temp = u0; u0 = u; u = temp;
var temp = v0; v0 = v; v = temp;
diffuse2(u,u0,v,v0, dt);
project(u, v, u0, v0);
var temp = u0; u0 = u; u = temp;
var temp = v0; v0 = v; v = temp;
advect(1, u, u0, u0, v0, dt);
advect(2, v, v0, u0, v0, dt);
project(u, v, u0, v0 );
}
var uiCallback = function(d,u,v) {};
function Field(dens, u, v) {
// Just exposing the fields here rather than using accessors is a measurable win during display (maybe 5%)
// but makes the code ugly.
this.setDensity = function(x, y, d) {
dens[(x + 1) + (y + 1) * rowSize] = d;
}
this.getDensity = function(x, y) {
return dens[(x + 1) + (y + 1) * rowSize];
}
this.setVelocity = function(x, y, xv, yv) {
u[(x + 1) + (y + 1) * rowSize] = xv;
v[(x + 1) + (y + 1) * rowSize] = yv;
}
this.getXVelocity = function(x, y) {
return u[(x + 1) + (y + 1) * rowSize];
}
this.getYVelocity = function(x, y) {
return v[(x + 1) + (y + 1) * rowSize];
}
this.width = function() { return width; }
this.height = function() { return height; }
// NK -- sorry Oliver, exposing them anyway
this.rawDensityArray = dens;
this.rowSize = function() { return rowSize; }
}
function queryUI(d, u, v) {
var i = size - 1; do {
u[i] = v[i] = d[i] = 0.0;
} while (i--);
uiCallback(new Field(d, u, v));
}
this.update = function () {
queryUI(dens_prev, u_prev, v_prev);
vel_step(u, v, u_prev, v_prev, dt);
dens_step(dens, dens_prev, u, v, dt);
displayFunc(new Field(dens, u, v));
}
this.setDisplayFunction = function(func) {
displayFunc = func;
}
this.iterations = function() { return iterations; }
this.setIterations = function(iters) {
if (iters > 0 && iters <= 100)
iterations = iters;
}
this.setUICallback = function(callback) {
uiCallback = callback;
}
var iterations = 10;
var visc = 0.5;
var dt = 0.1;
var dens;
var dens_prev;
var u;
var u_prev;
var v;
var v_prev;
var width;
var height;
var rowSize;
var size;
var displayFunc;
function reset()
{
rowSize = width + 2;
size = (width+2)*(height+2);
dens = new Array(size);
dens_prev = new Array(size);
u = new Array(size);
u_prev = new Array(size);
v = new Array(size);
v_prev = new Array(size);
for (var i = 0; i < size; i++)
dens_prev[i] = u_prev[i] = v_prev[i] = dens[i] = u[i] = v[i] = 0;
}
this.reset = reset;
this.setResolution = function (hRes, wRes)
{
var res = wRes * hRes;
if (res > 0 && res < 1000000 && (wRes != width || hRes != height)) {
width = wRes;
height = hRes;
reset();
return true;
}
return false;
}
this.setResolution(64);
}
<!doctype HTML>
<html class="no-js">
<head>
<meta charset="utf-8">
<title>Nick Kwiatek &mdash; Web &amp; interaction designer + developer</title>
<script>(function(H){H.className=H.className.replace(/\bno-js\b/,'js')})(document.documentElement)</script>
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<meta name="keywords" content="nick, kwiatek, nick kwiatek, design, web design, ux, user experience, interface, interface design, development, javascript, typography, dislikes when people use internet memes in public">
<meta name="description" content="Making interfaces fun and not annoying since 1998 (first with Apple Hypercard; now with the web).">
<meta name="author" content="Nick Kwiatek">
<meta name="copyright" content="2012">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
</head>
<body class="smaller">
<div id="overlay">
<div id="card">
<br/>
<br/>
<h1>Nick Kwiatek</h1><br/>
<h2>Web &amp; interaction designer + developer</h2><br/>
<h2>New York, NY</h2><br/>
<br/>
&rarr; <a href="mailto:nick@nkwiatek.com">Email me</a><br/>
&rarr; <a href="http://twitter.com/nkwiatek" target="_blank">Send me a tweet</a><br/>
<br/><br/><dl>
<dd>"People create programs to direct processes. In effect, we conjure the spirits of the computer with our spells." <br/><br/></dd>
<dt>&mdash;The Structure &amp; Interpretation<br/>of Computer Programs </dt>
</dl>
<br/>
<br/>
</div>
<br/>
<div id="controls">
<br/>
<button id="clear">"Disappear!"</button><br/>
<br/>
</div>
</div>
<p id="fluid" unselectable="on"></p>
<div id="fps">fps: 0</div>
<script type="text/javascript" src="fluid.solver.js"></script>
<script type="text/javascript" src="fluid.display.ascii.js"></script>
<script type="text/javascript">
jQuery(document).ready(function($) {
var fluid
, el
, display
, displayFPS
// physics solver
fluid = new FluidField();
// display DOM element
el = document.getElementById('fluid');
$(window)
.resize(function(e) {
var glyphDims = (function() {
var $dummy = $(el).clone().text('M').css({visibility: 'hidden'}).appendTo('body');
var retval = {
width: $dummy.width(),
height: parseInt( $dummy.css('line-height'), 10 ) // more reliable than height()
};
$dummy.remove();
return retval;
})();
// this won't be perfect, as the screen dims won't often be evenly disible
fluid.setResolution(Math.round($(window).height() / glyphDims.height), Math.round($(window).width() / glyphDims.width));
})
.trigger('resize');
// display component
display = new FluidDisplayASCII(fluid)
display.bindElement(el)
displayFPS = function() {
var frames = 0
, time_start = new Date()
, $DOMfps = $('#fps')
$DOMfps.show()
return function(frame) {
var time_end = new Date()
frames++
if ((time_end - time_start) >= 1000) {
$DOMfps.text('fps: ' + Math.round((1000 * frames / (time_end - time_start))))
time_start = time_end
frames = 0
}
}
}
// final configuration
//fluid.setIterations(15);
//display.Config.density = 260;
fluid.setIterations(5)
display.Config.density = 180
display.Animation.start( /*displayFPS()*/ )
// rest of initation
$('#overlay').hide().fadeTo('slow', 1)
$('button#clear').mousedown(function(e) {
var $el = $(el)
$el
.addClass('fading')
.fadeOut('medium', function() {
fluid.reset()
$el
.removeClass('fading')
.fadeIn('fast')
})
})
})
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-28391837-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>
* {
padding: 0;
margin: 0;
text-rendering: optimizeSpeed;
-webkit-font-smoothing: none;
}
a {
color: red !important;
text-decoration: underline;
}
a:hover {
text-decoration: none;
}
body {
overflow: hidden;
}
body * {
font-size: 12px;
line-height: 16px;
font-family: "Courier New";
color: #444;
}
body.smaller * {
font-size: 11px;
line-height: 15px;
}
#fluid {
color: red;
white-space: pre;
width: auto;
height: auto;
display: inline-block;
cursor: default;
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
user-select: none;
/*
-webkit-transition: all 0.3s ease-out;
-moz-transition: all 0.3s ease-out;
-o-transition: all 0.3s ease-out;
transition: all 0.3s ease-out;
*/
}
#fluid.fading {
font-style: italic;
}
#fps {
position: absolute;
bottom: 10px;
right: 10px;
background: white;
padding: 10px;
display: none;
}
.js #overlay {
display: none;
}
#overlay {
position: absolute;
top: 63px;
left: 70px;
pointer-events: none;
z-index: 100;
}
#overlay > div {
background: white;
/*padding: 0 27px 0 22px;*/
padding: 0 21px;
pointer-events: auto;
}
#card {
width: 287px;
}
#controls {
display: inline-block;
/*background: none !important;*/
}
body.smaller #overlay {
top: 61px;
}
h1, h2, p {
display: inline-block;
}
h2 {
font-weight: normal;
}
dd {
text-indent: -4px;
}
dt {
/*text-indent: -4px;*/
font-style: italic;
text-align: right;
}
button#clear {
background: red;
color: white;
font-weight: bold;
padding: 6px 7px;
border: 1px solid rgb(240, 0, 0);
cursor: pointer;
-webkit-box-shadow: 2px 2px 0px 0px rgb(210, 210, 210);
-moz-box-shadow: 2px 2px 0px 0px rgb(210, 210, 210);
box-shadow: 2px 2px 0px 0px rgb(210, 210, 210);
}
button#clear:hover {
position: relative;
left: -1px;
top: -1px;
-webkit-box-shadow: 3px 3px 0px 0px rgb(220, 220, 220);
-moz-box-shadow: 3px 3px 0px 0px rgb(220, 220, 220);
box-shadow: 3px 3px 0px 0px rgb(220, 220, 220);
}
button#clear:active {
position: relative;
left: 1px;
top: 1px;
-webkit-box-shadow: 0px 0px;
-moz-box-shadow: 0px 0px;
box-shadow: 0px 0px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment