Skip to content

Instantly share code, notes, and snippets.

@timyates
Created August 20, 2010 11:02
Show Gist options
  • Save timyates/540074 to your computer and use it in GitHub Desktop.
Save timyates/540074 to your computer and use it in GitHub Desktop.
(function( $ ) {
var defaults = {
image : 'water.png',
damping : 4,
dripSize : 9,
dripDepth : 250
};
var options ;
var canvas ;
var top = 0 ;
var left = 0 ;
var ctx ;
var img ;
var width ;
var height ;
var halfWidth ;
var halfHeight ;
var offsets = [] ;
var buffer = [] ;
var start ;
var end ;
var imageData ;
var surface ;
var surfaceData ;
$.fn.liquify = function( opts ) {
canvas = $(this) ;
options = $.extend( defaults, opts ) ;
top = canvas.offset().top ;
left = canvas.offset().left ;
ctx = canvas.get(0).getContext("2d");
img = new Image();
img.onload = init ;
img.src = options.image ;
}
var init = function() {
width = canvas.get( 0 ).width = img.width ;
height = canvas.get( 0 ).height = img.height ;
halfWidth = width >> 1 ;
halfHeight = height >> 1 ;
offsets = []
for( i = 0 ; i < height ; i++ ) {
offsets.push( i * width * 4 ) ;
}
buffer = []
for( i = 0 ; i < width * ( height + 2 ) * 2 ; i++ ) {
buffer.push( 0 ) ;
}
start = width ;
end = width * ( height + 3 ) ;
canvas.mousemove( mouseMove )
ctx.clearRect( 0, 0, width, height ) ;
ctx.drawImage( img, 0, 0, width, height ) ;
imageData = ctx.getImageData( 0, 0, width, height ).data ;
surface = ctx.createImageData( width, height ) ;
surfaceData = surface.data ;
ctx.putImageData( surface, 0, 0 ) ;
setTimeout( function() { loop() }, 1 ) ;
}
var mouseMove = function( e ) {
var x = e.clientX - left ;
var y = e.clientY - top ;
x -= options.dripSize >> 1 ;
y -= options.dripSize >> 1 ;
if( x > width - options.dripSize ) x = width - options.dripSize ;
if( x < 0 ) x = 0 ;
if( y > height - options.dripSize ) y = height - options.dripSize ;
if( y < 0 ) y = 0 ;
addRipple( x, y )
}
var addRipple = function( x, y ) {
var size = options.dripSize ;
var depth = options.dripDepth ;
for( xx = 0 ; xx < size ; xx++ ) {
for( yy = 0 ; yy <= size ; yy++ ) {
if( y + xx + 1 > 0 && y + xx + 1 < height - size ) {
buffer[ end + ( offsets[ y + xx ] >> 2 ) + x + yy ] += depth ;
}
}
}
}
var loop = function() {
process() ;
swap() ;
setTimeout( function() { loop() }, 20 ) ;
}
var swap = function() {
var tmp = start ;
start = end ;
end = tmp ;
}
var process = function() {
var st = start ;
var pos = 0 ;
var en = end ;
var damp = options.damping ;
var rnd = Math.floor( Math.random() * 255 ) ;
for( y = 0 ; y < height ; y++ ) {
for( x = 0 ; x < width ; x++ ) {
var val = ( buffer[ st - width ] +
buffer[ st + width ] +
buffer[ st - 1 ] +
buffer[ st + 1 ] ) ;
val = val >> 1 ;
val -= buffer[ en ] ;
val -= val >> damp ;
buffer[ en++ ] = val ;
val = 1024 - val ;
var dx = ( ( x - halfWidth ) * val >> 10 ) + halfWidth ;
dx = dx < width ? dx >= 0 ? dx : 0 : width - 1 ;
var dy = ( ( y - halfHeight ) * val >> 10 ) + halfHeight ;
dy = dy < height ? dy >= 0 ? dy : 0 : height - 1 ;
var ps = ( ( dx * 4 ) + offsets[ dy ] ) ;
ps = ps < 0 ? 0 : ps >= imageData.length - 4 ? imageData.length - 5 : ps ;
for( i = 0 ; i < 4 ; i++ ) {
surfaceData[ pos + i ] = imageData[ ps + i ] ;
}
pos += 4 ;
st++ ;
}
}
ctx.putImageData( surface, 0, 0 ) ;
}
})( jQuery ) ;
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
<script src="jquery-durius-water.js" type="text/javascript"></script>
<title>Water</title>
</head>
<body>
<canvas id='water'></canvas>
</body>
<script type="text/javascript">
$(document).ready( function() {
$('#water').liquify( {
image: 'logo.jpg'
} ) ;
} ) ;
</script>
</html>
@timyates
Copy link
Author

Please note as well, that this code is slow.

This could be due to:

  • shoddy programming by me
  • the amount of processing it has to do
  • or the speed of the putImageData call

I haven't done any profiling to find out which one it is (though I suspect a bit of each)

@timyates
Copy link
Author

Improved the speed by getting rid of all the options.XXX calls, and just having internal vars for each property

Also, changed surface.data[] to surfaceData inside the main processing loop

This actually runs quite fast on Chrome now, sluggishly on Safari, and like a sock cutting through treacle on Firefox

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment