Faster reflecions

Recently, Thomas Fuchs came up with a nice prototype/scriptaculous-based piece of code, the Reflector, which renders shiny image reflections in the browser.

However, it’s performance so far is a bit weak. It is a proof of concept and Thomas himself calls it “not really meant for production use yet”. In the following you find an (speedwise) improved version, which boosts rendering time by a factor of 2–3.
Demo and comparison page

In Thomas’ original code the reflected image consists of a stack of divs, each of one pixel height and containing a shifted copy of the original image. The code loops over the rows of the image, creating and attaching one corresponding reflection row div at each cycle.

So, changes have been made as follows:

  • the reflected image is constructed out of divs with (shifted) background images, reducing DOM complexity
  • the constructed divs are attached to the DOM only after all divs have been created, thus avoiding unnecessary updates of the browser view
  • added a fade option, which determines the fading behaviour. By default it’s set to 1, that is, a linear fade. A quadratic behaviour (fade:2) seems to look more natural (idea by jakdak), try it out and judge yourself.
    Setting fade to zero disables the fading.

Tested with Safari, Firefox and IE (6.0). The scriptaculous setOpacity()-function seems not to work in Opera, the reflected image is not fading.

Feel free to use this code as you like, comments are very much appreciated.

var Reflector = {
  reflect: function(element) {
    element = $(element);
    options = $H({
      amount: 1/3,
      opacity: 1/3,
      fade: 1
    }).merge(arguments[1] || {});

    var p = element.parentNode, n = element.nextSibling;
    var d = 1.0/(element.height*options.amount);
    var f = document.createDocumentFragment();

    (element.height*options.amount).times( function(line) {
      var h = Builder.node('div',{
        style: 'height:1px; width:'+element.width+'px;'
              +'background-image:url('+element.src+');'
              +'background-position:0 '+(line+1)+'px;'
              +'overflow:hidden;'
      });
      $(h).setOpacity(Math.pow(1-d*line,options.fade)*options.opacity); // IE needs the $()
      f.appendChild(h);
    });

    // update the view now
    p.insertBefore(f,n);
  }
}

To use the Reflector you could insert something like this into your page:
Event.observe(window,'load', function(){Reflector.reflect('myImage');});.


About this entry