Quando gli Applet Java non erano ancora estinti e ci si deliziava a cercare effetti accattivanti per il proprio sito web una delle magie più diffuse era l'effetto "acqua": al passaggio del mouse sull'applet questo si "increspava" come uno specchio d'acqua o un piccolo stagno sfiorato da un dito.

Non si doveva aspettare molto per vederne una versione che utilizzasse le ultime tecnologie HTML5 (canvas, in questo caso) e i nuovi engine JavaScript dei nostri browser più moderni: grazie alle capacità di Sergey Chikuyonok – insieme ad altri, ma la sua versione è la più efficiente ed efficace – possiamo goderci questo simpatico effetto, integrandolo anche sulle nostre pagine per aggiungere un tocco "zen" ad immagini e canvas.

Chiaramente uno degli aspetti più interessanti di ogni applicazione HTML5/JavaScript è che il codice è disponibile per tutti: dando un'occhiata a quello che scrive Sergey possiamo cogliere che l'effetto viene ottenuto "spostando" i pixel disturbati (sia in maniera random che al passaggio del mouse) in un intorno circolare rispetto al punto di disturbo con l'ampiezza di spostamento dei pixel che si riduce al crescere del raggio (le "onde" si "smorzano")

Il codice è relativamente semplice e corto, visibile all'indirizzo http://js1k.com/2010-first/demo/131 (dove è presente anche un simpatico demo di cui vediamo un'immagine sopra).

E' possibile personalizzare questo effetto per inserirlo sulle proprie pagine ad esempio indicando un'immagine come "sfondo dello stagno", andando ad intervenire sulle righe

    with (ctx) {<br />            fillStyle = '#a2ddf8';<br />            fillRect(0, 0, width, height);<br />           <br />            fillStyle = '#07b';<br />            save();<br />            rotate(-0.785);<br />            for (var i = 0; i < count; i++) {<br />                fillRect(-width, i * step, width * 3, line_width);<br />            }<br />           <br />            restore();<br />        }

utilizzando piuttosto

drawImage

ottenendo un risultato tipo:

Per facilitarvi (un po'…) la vita vi indichiamo qui il codice cmpleto che abbiamo usato per ottenere questo effetto: chiaramente dovrete giocare sull'immagine oltre che sulle dimensioni (e magari anche qualche altro parametro per avere più o meno onde e più o meno increspate) – per chi mastica un po' di JavaScript e HTML5, in ogni caso, dovrebbe essere facile come bere un bicchier d'acqua. Con le onde. 😉

<html><br />    <br />        <head><br />    <br />            <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script><br />    <br />            <script><br />                /**<br />                 * Water ripple effect.<br />                 * Original code (Java) by Neil Wallis<br />                 * @link http://www.neilwallis.com/java/water.html<br />                 *<br />                 * @author Sergey Chikuyonok (serge.che@gmail.com)<br />                 * @link http://chikuyonok.ru<br />                 */<br />                $(document).ready(function(){<br />                    var canvas = document.getElementById('c');<br />                        /** @type {CanvasRenderingContext2D} */<br />                    var ctx = canvas.getContext('2d'),<br />                        width = 150,<br />                        height = 150,<br />                        half_width = width >> 1,<br />                        half_height = height >> 1,<br />                        size = width * (height + 2) * 2,<br />                        delay = 30,<br />                        oldind = width,<br />                        newind = width * (height + 3),<br />                        riprad = 3,<br />                        mapind,<br />                        ripplemap = [],<br />                        last_map = [],<br />                        ripple,<br />                        texture,<br />                        line_width = 20,<br />                        step = line_width * 2,<br />                        count = height / line_width;<br />                       <br />                    canvas.width = width;<br />                    canvas.height = height;<br />                   <br />                    /*<br />                     * Water ripple demo can work with any bitmap image<br />                     * (see example here: http://media.chikuyonok.ru/ripple/)<br />                     * But I need to draw simple artwork to bypass 1k limitation<br />                     */<br />    <br />    <br />                    var five = document.getElementById("five");<br />                    ctx.drawImage(five, 0, 0);<br />    <br />                   <br />                    texture = ctx.getImageData(0, 0, width, height);<br />                    ripple = ctx.getImageData(0, 0, width, height);<br />                   <br />                    for (var i = 0; i < size; i++) {<br />                        last_map[i] = ripplemap[i] = 0;<br />                    }<br />                   <br />                    /**<br />                     * Main loop<br />                     */<br />                    function run() {<br />                        newframe();<br />                        ctx.putImageData(ripple, 0, 0);<br />                    }<br />                   <br />                    /**<br />                     * Disturb water at specified point<br />                     */<br />                    function disturb(dx, dy) {<br />                        dx <<= 0;<br />                        dy <<= 0;<br />                       <br />                        for (var j = dy - riprad; j < dy + riprad; j++) {<br />                            for (var k = dx - riprad; k < dx + riprad; k++) {<br />                                ripplemap[oldind + (j * width) + k] += 512;<br />                            }<br />                        }<br />                    }<br />                   <br />                    /**<br />                     * Generates new ripples<br />                     */<br />                    function newframe() {<br />                        var i, a, b, data, cur_pixel, new_pixel, old_data;<br />                       <br />                        i = oldind;<br />                        oldind = newind;<br />                        newind = i;<br />                       <br />                        i = 0;<br />                        mapind = oldind;<br />                       <br />                        // create local copies of variables to decrease<br />                        // scope lookup time in Firefox<br />                        var _width = width,<br />                            _height = height,<br />                            _ripplemap = ripplemap,<br />                            _mapind = mapind,<br />                            _newind = newind,<br />                            _last_map = last_map,<br />                            _rd = ripple.data,<br />                            _td = texture.data,<br />                            _half_width = half_width,<br />                            _half_height = half_height;<br />                       <br />                        for (var y = 0; y < _height; y++) {<br />                            for (var x = 0; x < _width; x++) {<br />                                data = (<br />                                    _ripplemap[_mapind - _width] +<br />                                    _ripplemap[_mapind + _width] +<br />                                    _ripplemap[_mapind - 1] +<br />                                    _ripplemap[_mapind + 1]) >> 1;<br />                                   <br />                                data -= _ripplemap[_newind + i];<br />                                data -= data >> 5;<br />                               <br />                                _ripplemap[_newind + i] = data;<br />    <br />                                //where data=0 then still, where data>0 then wave<br />                                data = 1024 - data;<br />                               <br />                                old_data = _last_map[i];<br />                                _last_map[i] = data;<br />                               <br />                                if (old_data != data) {<br />                                    //offsets<br />                                    a = (((x - _half_width) * data / 1024) << 0) + _half_width;<br />                                    b = (((y - _half_height) * data / 1024) << 0) + _half_height;<br />                   <br />                                    //bounds check<br />                                    if (a >= _width) a = _width - 1;<br />                                    if (a < 0) a = 0;<br />                                    if (b >= _height) b = _height - 1;<br />                                    if (b < 0) b = 0;<br />                   <br />                                    new_pixel = (a + (b * _width)) * 4;<br />                                    cur_pixel = i * 4;<br />                                   <br />                                    _rd[cur_pixel] = _td[new_pixel];<br />                                    _rd[cur_pixel + 1] = _td[new_pixel + 1];<br />                                    _rd[cur_pixel + 2] = _td[new_pixel + 2];<br />                                }<br />                               <br />                                ++_mapind;<br />                                ++i;<br />                            }<br />                        }<br />                       <br />                        mapind = _mapind;<br />                    }<br />                   <br />                    canvas.onmousemove = function(/* Event */ evt) {<br />                        disturb(evt.offsetX || evt.layerX, evt.offsetY || evt.layerY);<br />                    };<br />                   <br />                    setInterval(run, delay);<br />                   <br />                    // generate random ripples<br />                    var rnd = Math.random;<br />                    setInterval(function() {<br />                        disturb(rnd() * width, rnd() * height);<br />                    }, 700);<br />                   <br />                })();<br />    <br />            </script><br />    <br />        </head><br />    <br />        <body><br />            <canvas id="c" width="150" height="150"></canvas><br />            <img id="five" src="http://html5today.it/images/5.png" style="display: none;"><br />        </body><br />    <br />    </html>