Bokeh = function( container, width, height )
{
var _blobCount = 20;
var _pool = [];
var _blobs = [];
var _width = width;
var _height = height;
var _mainCanvas;
var _mainContext;
var _tempCanvas;
var _tempContext;
var _tempScale = 2;
var _hOffset = 0.2;
//...
function reset()
{
_mainCanvas.width = _width;
_mainCanvas.height = _height;
_tempCanvas.width = _width /_tempScale;
_tempCanvas.height = _height /_tempScale;
_mainContext = _mainCanvas.getContext( '2d' );
_mainContext.clearRect( 0, 0, _mainCanvas.width, _mainCanvas.height );
_tempContext = _tempCanvas.getContext( '2d' );
_tempContext.clearRect( 0, 0, _tempCanvas.width / _tempScale, _tempCanvas.height / _tempScale );
}
//...
function render()
{
_mainContext.clearRect( 0, 0, _mainCanvas.width, _mainCanvas.height );
_tempContext.clearRect( 0, 0, _tempCanvas.width, _tempCanvas.height );
_tempContext.globalCompositeOperation = "lighter";
var n = _blobs.length;
while( --n > -1 )
{
var blob = _blobs[ n ];
var expired = blob.paint( _tempContext );
if( expired )
{
expireBlob( blob );
}
}
if( _blobs.length < _blobCount && Math.random() > 0.8 )
{
var x = Math.random() * _width;
var y = Math.random() * _height;
_hOffset = ( _hOffset + 0.005 ) % 1;
var h = _hOffset + ( x / _width ) * 0.3;
var s = 0.6 + Math.random() * 0.4;
var v = 0.3;
var c = hsv2rgb( h, s, v );
createBlob( x / _tempScale, y / _tempScale, c );
}
var imageData = _tempContext.getImageData( 0, 0, _tempCanvas.width, _tempCanvas.height );
var data = imageData.data;
for( var iteration = 0; iteration < 2; ++iteration )
{
for( var i = 0, m = data.length; i < m; )
{
var a = 0;
var r = 0;
var g = 0;
var b = 0;
var total = 0;
var off = 4;
// indices 8 surrounding pixels
var data8 = [
i - off - 4, i - off, i - off + 4, // top
i - 4, i + 4, // middle
i + off - 4, i + off, i + off + 4 // bottom
];
// calculating Sum value of all close pixels
for( var e = 0; e < data8.length; e += 1 )
{
if( data8[ e ] >= 0 && data8[ e ] <= data.length - 3 )
{
a += data[ data8[ e ] ];
r += data[ data8[ e ] + 1 ] + 2;
g += data[ data8[ e ] + 2 ] + 2;
b += data[ data8[ e ] + 3 ] + 2;
total += 1;
}
}
data[ i++ ] = (a / total);
data[ i++ ] = (r / total);
data[ i++ ] = (g / total);
data[ i++ ] = (b / total);
}
}
_tempContext.putImageData( imageData, 0, 0 );
_mainContext.drawImage( _tempCanvas, 0, 0, _tempCanvas.width, _tempCanvas.height, 0,0, _mainCanvas.width, _mainCanvas.height );
setTimeout( render, 1000 / 60 );
}
init();
};
Blob = function()
{
var _x;
var _y;
var _size = 0;
var _r = 255;
var _g = 255;
var _b = 255;
var _vx = 0;
var _vy = 0;
var _alpha = 1;
var _lifeSpan = 0;
var _age = 0;
this.reset = function( x, y, color )
{
_x = x;
_y = y;
_r = color[ 0 ];
_g = color[ 1 ];
_b = color[ 2 ];
_vx = ( Math.random() - Math.random() ) * 0.3;
_vy = ( Math.random() - Math.random() ) * 0.3;
_size = 15 + Math.random() * 10;
_lifeSpan = 64 + Math.random() * 256;
_age = 0;
_alpha = 1;
};
this.paint = function( context )
{
_x += _vx;
_y += _vy;
_size += 0.1;
_alpha = Math.sin( ( 1.0 - ( _age / _lifeSpan ) ) * Math.PI );
var fillAlpha = _alpha * 0.8;
var strokeAlpha = 0.2 + _alpha * 0.8;
_r += 0.5;
_g += 0.5;
_b += 0.5;
var r = Math.floor( Math.min( 255, _r ) );
var g = Math.floor( Math.min( 255, _g ) );
var b = Math.floor( Math.min( 255, _b ) );
context.beginPath();
context.strokeStyle = "rgba(" + r + "," + g + "," + b + "," + strokeAlpha + ")";
context.fillStyle = "rgba(" + r + "," + g + "," + b + "," + fillAlpha + ")";
context.lineWidth = 1.5;
context.arc( _x, _y, _size, 0, Math.PI * 2, false );
context.fill();
context.stroke();
context.closePath();
_age++;
return ( _age >= _lifeSpan )
};
};