
Water ripple effect with HTML5. Today we continue JavaScript examples, and our article will about using javascript in modeling of water effects. This will emulation of water drops at images. We should click at image in desired place to see this effect. Sometimes we can create very interesting solutions using ordinary Javascript (of course for HTML) 🙂
Here are sample and downloadable package:
Live Demo
[sociallocker]
download in package
[/sociallocker]
Ok, download the example files and lets start coding !
Step 1. HTML
As usual, we start with the HTML.
This is our main page code with all samples.
index.html
<!DOCTYPE html> <html> <head> <meta charset=utf-8 /> <title>Water drops effect</title> <link rel="stylesheet" href="css/main.css" type="text/css" /> <script src="js/vector2d.js" type="text/javascript" charset="utf-8"></script> <script src="js/waterfall.js" type="text/javascript" charset="utf-8"></script> </head> <body> <div class="example"> <h3><a href="#">Water drops effect</a></h3> <canvas id="water">HTML5 compliant browser required</canvas> <div id="switcher"> <img onclick='watereff.changePicture(this.src);' src="data_images/underwater1.jpg" /> <img onclick='watereff.changePicture(this.src);' src="data_images/underwater2.jpg" /> </div> <div id="fps"></div> </div> </body> </html>
Step 2. CSS
Here are used CSS styles.
css/main.css
body{background:#eee;margin:0;padding:0} .example{background:#FFF;width:600px;border:1px #000 solid;margin:20px auto;padding:15px;-moz-border-radius: 3px;-webkit-border-radius: 3px} #water { width:500px; height:400px; display: block; margin:0px auto; cursor:pointer; } #switcher { text-align:center; overflow:hidden; margin:15px; } #switcher img { width:160px; height:120px; }
Step 3. JS
Here are our main control JS file.
js/main.js
function drop(x, y, damping, shading, refraction, ctx, screenWidth, screenHeight){ this.x = x; this.y = y; this.shading = shading; this.refraction = refraction; this.bufferSize = this.x * this.y; this.damping = damping; this.background = ctx.getImageData(0, 0, screenWidth, screenHeight).data; this.imageData = ctx.getImageData(0, 0, screenWidth, screenHeight); this.buffer1 = []; this.buffer2 = []; for (var i = 0; i < this.bufferSize; i++){ this.buffer1.push(0); this.buffer2.push(0); } this.update = function(){ for (var i = this.x + 1, x = 1; i < this.bufferSize - this.x; i++, x++){ if ((x < this.x)){ this.buffer2[i] = ((this.buffer1[i - 1] + this.buffer1[i + 1] + this.buffer1[i - this.x] + this.buffer1[i + this.x]) / 2) - this.buffer2[i]; this.buffer2[i] *= this.damping; } else x = 0; } var temp = this.buffer1; this.buffer1 = this.buffer2; this.buffer2 = temp; } this.draw = function(ctx){ var imageDataArray = this.imageData.data; for (var i = this.x + 1, index = (this.x + 1) * 4; i < this.bufferSize - (1 + this.x); i++, index += 4){ var xOffset = ~~(this.buffer1[i - 1] - this.buffer1[i + 1]); var yOffset = ~~(this.buffer1[i - this.x] - this.buffer1[i + this.x]); var shade = xOffset * this.shading; var texture = index + (xOffset * this.refraction + yOffset * this.refraction * this.x) * 4; imageDataArray[index] = this.background[texture] + shade; imageDataArray[index + 1] = this.background[texture + 1] + shade; imageDataArray[index + 2] = 50 + this.background[texture + 2] + shade; } ctx.putImageData(this.imageData, 0, 0); } } var fps = 0; var watereff = { // variables timeStep : 20, refractions : 2, shading : 3, damping : 0.99, screenWidth : 500, screenHeight : 400, pond : null, textureImg : null, interval : null, backgroundURL : 'data_images/underwater1.jpg', // initialization init : function() { var canvas = document.getElementById('water'); if (canvas.getContext){ // fps countrt fps = 0; setInterval(function() { document.getElementById('fps').innerHTML = fps / 2 + ' FPS'; fps = 0; }, 2000); canvas.onmousedown = function(e) { var mouse = watereff.getMousePosition(e).sub(new vector2d(canvas.offsetLeft, canvas.offsetTop)); watereff.pond.buffer1[mouse.y * watereff.pond.x + mouse.x ] += 200; } canvas.onmouseup = function(e) { canvas.onmousemove = null; } canvas.width = this.screenWidth; canvas.height = this.screenHeight; this.textureImg = new Image(256, 256); this.textureImg.src = this.backgroundURL; canvas.getContext('2d').drawImage(this.textureImg, 0, 0); this.pond = new drop( this.screenWidth, this.screenHeight, this.damping, this.shading, this.refractions, canvas.getContext('2d'), this.screenWidth, this.screenHeight ); if (this.interval != null){ clearInterval(this.interval); } this.interval = setInterval(watereff.run, this.timeStep); } }, // change image func changePicture : function(url){ this.backgroundURL = url; this.init(); }, // get mouse position func getMousePosition : function(e){ if (!e){ var e = window.event; } if (e.pageX || e.pageY){ return new vector2d(e.pageX, e.pageY); } else if (e.clientX || e.clientY){ return new vector2d(e.clientX, e.clientY); } }, // loop drawing run : function(){ var ctx = document.getElementById('water').getContext('2d'); watereff.pond.update(); watereff.pond.draw(ctx); fps++; } } window.onload = function(){ watereff.init(); }
As you can see- I using vector2d function here. This function available in ‘vector2d.js’ (in our package). Another code – pretty difficult – pure mathematics. But you are welcome to make experiments here.
Live Demo
Conclusion
Hope that you was happy to play with it. I hope that water drops looks fine 🙂 If is you were wondering – do not forget to thank. I would be grateful for your interesting comments. Good luck!