3.30.2010

Horizontal Mouse Wheel / Swipe Gesture Support in Flash

It's possible to detect horizontal scrolling actions from the mouse or MacBook touch pads with a little JavaScript magic. Caveat: This only works on FireFox 3.1+ and Webkit-based browsers (Chrom{e,ium} and Safari). Microsoft still has not extended their events to send horizontal scroll messages*.

To do this, we're going to inject JS dynamically at runtime. I'm only giving you code snippets, you'll have to tie the logic together yourself.

First, you'll want to make sure you have a working ExternalInterface and add a callback to handle scroll events coming from JS:
if (ExternalInterface.available && ExternalInterface.objectID != null)
{
    try {
        ExternalInterface.call('eval', 'true;');
        ExternalInterface.addCallback("scrollEvent", onScrollEvent);
    } catch (e:Error){
        // nope!
    }
}

Here's your handler:
function onScrollEvent(xDelta:Number, yDelta:Number):void
{
    // scroll something in a fancy manner by xDelta and yDelta
    // note: Webkit may give you TWO non-zero values as it allows
    // simultaneous scrolling
}

Now we need to drop the JS into a String const:
const SCROLL_SCRIPT:String = (<![CDATA[
    function setupScrolling(objectID)
    {
        var flashObject = document.getElementsByName(objectID)[0];
        var eventListenerObject = flashObject;
        var isWebkit = false;

        if (navigator && navigator.vendor)
        {
            isWebkit = navigator.vendor.match("Apple") || navigator.vendor.match("Google");
        }
        
        // some events will need to point to the containing object tag
        if (isWebkit && flashObject.parentNode.tagName.toLowerCase() == "object")
        {
            eventListenerObject = flashObject.parentNode;
        }
        
        var scrollHandler = function(event)
        {
            var xDelta = 0;
            var yDelta = 0;
            
            // IE special case
            if (!event)
                event = window.event;
            
            // IE/Webkit/Opera
            if (event.wheelDelta)
            {
                // horizontal scrolling is supported in Webkit
                if (event.wheelDeltaX)
                {
                    // Webkit can scroll two directions simultaneously
                    xDelta = event.wheelDeltaX;
                    yDelta = event.wheelDeltaY;
                }
                else
                {
                    // fallback to standard scrolling interface
                    yDelta = event.wheelDelta;
                }
    
                // you'll have to play with these,
                // browsers on Windows and OS X handle them differently
                xDelta /= 120;
                yDelta /= 120;
    
                // Opera special case
                if (window.opera)
                {
                    yDelta = -yDelta;
                    // Opera doesn't support hscroll; vscroll is also buggy
                }
            }
            // Firefox (Mozilla)
            else if (event.detail)
            {
                yDelta = -event.detail/1.5;
                // hscroll supported in FF3.1+
                if (event.axis)
                {
                    if (event.axis == event.HORIZONTAL_AXIS)
                    {
                        // FF can only scroll one dirction at a time
                        xDelta = yDelta;
                        yDelta = 0;
                    }
                }
            }
    
            try
            {
                flashObject.scrollEvent(xDelta, yDelta);
            }
            catch(e) {};

            if (event.preventDefault)
                event.preventDefault();
            event.returnValue = false;            
        }
        
        if (window.addEventListener)
        {
            // not IE
            eventListenerObject.addEventListener('mouseover', function(e)
            {
                if (isWebkit)
                {
                    window.onmousewheel = scrollHandler;
                }
                else
                {
                    window.addEventListener("DOMMouseScroll", scrollHandler, false);
                }
            }, false);
        }
        else
        {
            // IE
            flashObject.onmouseover = function(e)
            {
                document.onmousewheel = scrollHandler;
            };
        }
    }]]>).toString();

Finally, execute it in the browser:
ExternalInterface.call("eval", SCROLL_SCRIPT);
ExternalInterface.call("eval", 'setupScrolling( "' + ExternalInterface.objectID + '" );');

Try it out (click the logo to look at my other documents at Scribd!) -- zoom in as far as you can go:



---
* Poor man's support for IE: Use a modifier key to enable horizontal scrolling. For instance, vertical scrolling works as usual -- and if you want hscroll, hold down CTRL while vscrolling.

No comments: