Email Updates RSS Subscribe
Line

This blog is created and maintained by the technical team at Hook in an effort to preserve and share the insights and experience gained during the research and testing phases of our development process. Often, much of this information is lost or hidden once a project is completed. These articles aim to revisit, expand and/or review the concepts that seem worth exploring further. The site also serves as a platform for releasing tools developed internally to help streamline ad development.

Launch
Line

Hook is a digital production company that develops interactive content for industry leading agencies and their brands. For more information visit www.byhook.com.

Line

Flash Mouse Wheel Support

Line
Posted on April 9th, 2010 by Jake
Line

Ah, the mouse scroll wheel. It used to be so simple, and so obvious. Couldn’t we have just left it alone, and come to some gentleman’s agreement on how the browsers notify your objects of what the user just did with the wheel?

Sadly however, the reality is that this is not the case. Different browsers fire the event in different manners and with different deltas. Whats worse, is depending on the hardware used to “scroll” with, the deltas can be seriously varied. For instance Safari 531.21.8 on windows has a raw delta value of 120, while on the mac it has a raw delta value of 12. However if you scroll fast enough, that delta value can reach a pretty high number. Whats worse, is that is you have a multi-touch track pad on your Mac or a “Magic Mouse”, the numbers are again different.

Additionally Safari will fire at least two events for every wheel notch, while FireFox will only fire one. Add in the fact that when embedding a flash object, the wmode setting has a major impact on how the flash object is drawn and how the events are handled, we have an unmanageable matrix of possible hardware/software/implementation combinations.

With just flash alone, there isn’t much you can do about even getting those scroll events in certain wmodes, and if you do, its impossible to know what kind of numbers you are going to be getting.

So in comes JavaScript to the rescue. We can set up Javascript listeners and force that to call methods in the swf. This is starting to sound better, but you still run into issues with deployment. Depending on the service used to distribute your swf, say an ad banner distribution company, you won’t be able to put custom Javascript in the html. This is where the Javascript Injection technique becomes the weapon of choice.

Download Classes Here.

Get Adobe Flash player

First the demo. If you scroll with a mouse wheel, or a multitouch pad, or whatever you have that triggers scroll events in the browser, you will see the swf react. The list of deltas represents what was sent to flash from the browser. The event count is the total number of events fired so far. The “Use Raw Values” and “Use Scaled Values” buttons will send the swf either the browsers delta values, or a scaled version of the delta number based on the rules of the script. The text field at the bottom will show you what browser information it has detected. Lastly you can click “Clear” to clear out the old delta values from the list.

If you view the source on the page, you will not see any code related to handling the flash scrolling. That is because it was injected into browser memory after the page has been rendered. When looking for ways to do this, we came across two very good resources:
http://blog.earthbrowser.com/2009/01/simple-solution-for-mousewheel-events.html
and
http://www.actionscript.org/resources/articles/745/1/JavaScript-and-VBScript-Injection-in-ActionScript-3/Page1.html

Much of the code we are using came from the Earth Browser blog, but we added some safety checking, and a few other niceties to make it a littler easier to implement and use.

Setup for this is very easy. Simply include this line somewhere early in your swf startup:

MouseWheelEnabler.init(stage);

Be sure to include the import:

import com.jac.mouse.MouseWheelEnabler;

That’s all there is to it. This should work on different browsers, platforms, and window modes. The MouseWheelEnabler will do the rest. When it gets a call from the Javascript, it builds a new mouse event, and dispatches it from whatever flash element the mouse is over at the time.

There are two parts to the actual injection, that I will mention briefly. The first is the registerJS() method in MouseWheelEnabler.as:

private static function registerJS() : void
        {
            if( ExternalInterface.available )
            {
                var id:String = 'mws_' + Math.floor(Math.random()*1000000);
                ExternalInterface.addCallback(id, function():void{});
                ExternalInterface.call(MouseWheelEnabler_JavaScript.CODE);
                ExternalInterface.call("mws.InitMouseWheelSupport", id);
                ExternalInterface.addCallback('externalMouseEvent', handleExternalMouseEvent);
            }
        }

This is called from the init() method mentioned earlier. The first thing this does is make a new “class” and adds it to the DOM by using the addCallback() method. The only real reason this is required is so that we can identify the swf that has been embeded. Once we can find the swf, we can check the properties on the swf to get info like which wmode (if any) is in use.

Next ExternalInterface.call() is used to do the actual “injection”. This is essentially adding all of the Javascript that manages the events at the browser level to the current DOM. If you look at the bottom of the MouseWheelEnabler.as file, there is a class defined called MouseWheelEnabler_JavaScript. In it is simply a constant that has an HTML <script> tag that contains all of the JS code. When this is executed with the call() method, it defines an anonymous function, then creates a new namespace. This namespace is how we talk to the newly defined JS methods and properties.

So what kinds of things did we add that can make your life easier? Well a few things, such as the BrowserInfo object, the userRawValues flag, and the eventTimeout.

First up, the BrowserInfo object. This bit of code is used to fill in the “Browser Info Detected” portion of the demo:

var browserInfo:BrowserInfo = MouseWheelEnabler.getBrowserInfo();
if (browserInfo)
{//show
      _detectedText.text = (browserInfo.browser + " / " + browserInfo.platform + " / " + browserInfo.version);
}//show

When MouseWheelEnabler.getBrowserInfo() is called, it talks to the browser through the injected JS and gets back a few different objects that contain info about the browser. These objects are taken, broken down, and then stored in a brand new BrowserInfo object for easy consumption. BrowserInfo has a three properties and a bunch of public constants. The properties are myInfoObject.platform, myInfoObject.browser, myInfoObject.version. These can be compared against the constants that are provided in the BrowserInfo class:

Next up is the useRawValues property on the MouseWheelEnabler class:

MouseWheelEnabler.useRawValues = true;

This will instruct the MouseWheelEnabler to grab the real value that was returned from the browser if set to true. If this is set to false, the JS will take the browser value, and divide it to scale it down to a smaller number. For instance it will take the 120 from safari and make it 10, or whatever you want the divisor to be, on a per browser basis. To change these divisors, see the if(event.wheelDelta) section of the injected JS. There are some example rules in there to demonstrate how to set divisors based on browsers.

Lastly is the eventTimeout property on the MouseWheelEnabler class.

//This is in milliseconds
//This is why you don't see multiple events firing in the demo above.
MouseWheelEnabler.eventTimeout = 50;

This is used to help reduce the number of events fired from the browser to one per wheel notch. Its basically an event throttling system. By default the timeout is set to 50 milliseconds, which seems to be long enough to skip the second events fired by some of the browser combination. If you set this to zero, you will get all of the events that are fired.

That pretty much does it for this round. Good luck, and as always let us know if you run into any issues, in the comment section below.

Line
60 Responses to “Flash Mouse Wheel Support”
« Older Comments
  1. Hugo says:

    OMG, I need to donate !

  2. Nenad says:

    Many Thanks Jake, it works like charm. I was straggling to find good Mouse Wheel support for Safari.

    Thanks Again!

  3. Ryan says:

    Awesome!!!! Looked high and low for something that worked on Chrome, IE, Firefox, Safari and Opera and this is the only one!!!

  4. Tony says:

    It works well in “transparent” wmode.
    Problem solved. Thanks Jake.

  5. Niels says:

    I’ve been looking for a solid solution for the mousewheel problem and yours if by far the best. Thanks!

  6. Slawson says:

    This worked very well – even with my stub loaded main swf app… however I also wanted to use the Ctrl/Cmnd key switch to allow my users fine control over the mouse wheeled displayObject. i.e. normal delta is 3 and with crtl it is 1.5 BUT with the crtrl key pressed it triggers the browser function of page zoom. Do you have an javaScript to defeat that as well?

    Much thanks Steve

  7. Pink says:

    Hi,

    I have a legacy project built in AS2 I would like to add this functionality to. It is way too large of a project to rewrite in AS3 with my project timeline. Has anybody seen anything comparable to this that is written in AS2?

    Thanks in advance

    massivex(at)sympatico(dot)ca

« Older Comments

Leave a Reply

*

Line
Line
Pony