Another Reflector – blazingly fast
As you may have noticed, even the improved version of the scriptaculous Reflector is too slow to be used on pages with more than just a few small images.
So I took the opportunity to look at a Javascript subject that so far had escaped my studies, that is, the Safari/Gecko canvas tag. The canvas tag offers powerful drawing capabilities which are performed right in the browser, have a look at some of it’s capabilities.
Of course, IE goes a different way and does not support canvas. Still, by applying its CSS filter attribute one can get a similar reflection effect.
While I was working on the new version and already had a first working version I came across the nice reflection.js by Cow, which does exactly what I was trying to get. Exactly the same way I was trying to do it. And it worked. I was too late.
I thus scrapped my code and took reflection.js, modified it a bit and fixed some bugs. Here it is, have a look at the demo page.
Compared to the scriptaculous version, this code seems longer and of course less elegant. But it is still pretty straightforward, possibly except for the scalings and offsets. You also have to keep in mind that there are actually two routines in it, for both Safari/Gecko and IE.
Since both the canvas tag and the IE CSS filter only support linear gradients, the fade option introduced in my “faster reflections” version had to be dropped, sorry.
var Reflector = {
reflect: function(element) {
element = $(element);
options = $H({
amount: 1/3,
opacity: 1/3
}).merge(arguments[1] || {});
// ******************************************************
// the following code is based on "reflection.js" v1.0 by
// Cow http://cow.neondragon.net
// Gfx http://www.jroller.com/page/gfx/
// Sitharus http://www.sitharus.com
// Andreas Linde http://www.andreaslinde.de/
//
// modifications & fixes by Julian Dreissig
// ******************************************************
// compute size of reflected and total image size
var reflectionWidth = element.width;
var reflectionHeight = options.amount * element.height;
var divHeight = (options.amount) * element.height;
// replace image element by div containing the original image plus the reflection
var d = Builder.node('div', {
style: 'width:'+reflectionWidth+'px;'
+ 'height:'+divHeight+'px;'
});
element.parentNode.replaceChild(d, element);
d.appendChild(element);
// create the reflection
if (document.all && !window.opera) {
// Internet Exploder: apply MS filter via CSS
var reflection = Builder.node('div', {
style: 'width:'+reflectionWidth+'px; '
+ 'height:'+reflectionHeight+'px; '
+ 'background:url('+element.src+') 0 -'+(element.height-reflectionHeight)+'px; '
+ 'filter:flipv progid:DXImageTransform.Microsoft.Alpha(opacity='+(options.opacity*100)+', style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy='+element.height+');'
});
d.appendChild(reflection);
} else {
// Intelligent browsers (FF, Safari...): create canvas element
var canvas = Builder.node('canvas', {
style: 'width:'+reflectionWidth+'px; '
+ 'height:'+reflectionHeight+'px;',
width: reflectionWidth,
height: reflectionHeight
});
d.appendChild(canvas);
var context = canvas.getContext("2d");
context.save();
// Firefox needs some offset tweaking
var ffOffset = /WebKit/.test(navigator.appVersion) ? 0 : -1;
// mirror and draw image
context.translate(0,reflectionHeight*2+ffOffset);
context.scale(1,-1); // mirroring along x-axis
context.drawImage(element, 0, element.height-reflectionHeight, reflectionWidth, reflectionHeight, 0, reflectionHeight, reflectionWidth, reflectionHeight);
// create fading gradient
var gradient = context.createLinearGradient(0, reflectionHeight, 0, 2*reflectionHeight);
gradient.addColorStop(0, "rgba(255, 255, 255, 1)"); // black
gradient.addColorStop(1, "rgba(255, 255, 255, "+(1-options.opacity)+")");
context.globalCompositeOperation = "destination-out";
context.fillStyle = gradient;
if (/WebKit/.test(navigator.appVersion))
context.fill();
else
context.fillRect(0, reflectionHeight+ffOffset, reflectionWidth, 2*reflectionHeight);
context.restore();
}
}
}
Again, feel free to use the code as you like and/or drop me a comment. Please note that reflection.js is published under a MIT-style license, which I thereby adopt for my code: MIT-licensed it is.
Yes, in the meantime reflection.js has evolved to version 1.5, fixing the bugs I also had fixed and more. So my code is not even worth a bug report. I really have some bad timing…
About this entry
You’re currently reading “Another Reflector – blazingly fast,” an entry on julian.dreissig.net
- Published:
- 05.21.06 / 9pm
- Category:
- Javascript
Comments are closed
Comments are currently closed on this entry.