Scaling Flash Movies to match Browser Zoom Levels
Recently I was tasked with figuring out how to scale the Flash assets in the web application I'm working on. In the app, there's two different Flash assets: a Spotlight (cycles through images) and a Video Player. Before I started working on the issue, our assets would stay the same fixed size no matter what the browser zoom level. You can see this issue in action by going to Hulu or Fancast and zooming in/out (Command +/- on Mac, Control +/- on Windows). The Flash assets don't scale with the browser's text.
I found a lot of references for how to trap and handle resizing in JavaScript, so that's the initial path I took. I ended up having issues trapping the resize event in IE, as well as persisting the appropriate zoom level on page reload. Because of this, I ended up using a pure ActionScript solution that works much better. This article shows how I implemented both solutions.
Regardless of implementation, the first change I had to make was to move the height and width from the Flash asset (object/embed/JS) to its surrounding tag (<section> in our app). Then I changed the height/width to 100% on the Flash asset.
JavaScript Implementation
To allow zooming in ActionScript, I modified our main class to expose a "zoom" method to JavaScript:
ExternalInterface.addCallback("zoom", _zoom); ... private function _zoom(scale:Number):void { _view.scaleX = _view.scaleX * scale; _view.scaleY = _view.scaleY * scale; }In the code above, _view refers to the container that holds all the items in the player. To call this method in JavaScript, I added the following code:
var windowHeight; var documentHeight; $(document).ready(function() { ... windowHeight = $(window).height(); documentHeight = $(document).height(); $(window).resize(resizeWindow); } // Resize Flash assets when page is zoomed function resizeWindow() { var newWindowHeight = $(window).height(); var newDocumentHeight = $(document).height(); // if document height hasn't changed, it's a browser window resize // event instead of a text zoom - don't change anything if (newDocumentHeight === documentHeight) { return; } else { documentHeight = newDocumentHeight; } var scale = (windowHeight / newWindowHeight); var player = getFlashMovie('playerId'); if (player && player.zoom) { player.zoom(scale); } var spotlight = getFlashMovie('spotlightId'); if (spotlight && spotlight.zoom) { spotlight.zoom(scale); } windowHeight = newWindowHeight; }
This seemed to work well in Firefox, Safari and Opera, but not in IE. I found this explanation about why it might not work, but I was unsuccessful in getting IE to recognize a resize/zoom event.
To fix scaling in our Spotlight asset, I used a similar solution. However, since the Spotlight didn't have all its elements in a container (they were being added directly to the stage), I had to refactor the code to add a SpotlightView (extends Sprite) that contains the bulk of the code.
Browsers persist the zoom level you've set for a site. The problem with the solution used here is it only scales up and down properly if you start from scale = 1 and revert to scale = 1 before leaving the site. If you zoom in and close your browser, when you come back the flash movies will be scale = 1 while the rest of the site is zoomed in. To solve this problem, I attempted to save the scale value in a cookie. This worked, and I was able to read the cookie in the *.as files to scale the movie correctly. However, I experienced some issues with this approach and didn't like having to delete cookies when I wanted the Flash assets to scale correctly.
ActionScript Implementation
After discovering issues with the JavaScript implementation, I did some research to see if it was possible to listen for the browser resize event in ActionScript. The Flash Fluid Layouts and Stage Resize in AS3 tutorial clued me in that the stage could listen for a resize event.
stage.addEventListener(Event.RESIZE, resizeListener);
After adding the above line in the initialization, I added a resizeListener function that scales based on the default dimensions. It also ensures no scaling happens in full screen mode.
private function resizeListener(e:Event):void { // don't scale if entering full screen mode if (stage.displayState == StageDisplayState.FULL_SCREEN) { _view.scaleX = 1; _view.scaleY = 1; } else { _view.scaleX = stage.stageWidth / 964; _view.scaleY = stage.stageHeight / 586; } }
For the Spotlight asset, there are a number of different layouts (home, featured and news). The main class has a resizeListener function that scales accordingly to which layout type is being used.
private function resizeListener(e:Event):void { var type:String = _view.getLayoutType(); if (type == "featured") { _view.scaleX = stage.stageWidth / 958; _view.scaleY = stage.stageHeight / 180; } else if (type == "home") { _view.scaleX = stage.stageWidth / 964; _view.scaleY = stage.stageHeight / 428; } else if (type == "news") { _view.scaleX = stage.stageWidth / 964; _view.scaleY = stage.stageHeight / 189; } }
Because the layout type isn't set until the XML is loaded, I listen for that event in my URLLoader.
xmlLoader.addEventListener(Event.COMPLETE, resizeListener);
With the pure ActionScript implementation, the zoom level is automatically persisted. The Event.RESIZE event is fired by the Flash plugin when the page first loads if the dimensions are not the default.
That's it! Special thanks to James Ward for clueing me into scaleX and scaleY. Hopefully Hulu and Comcast can use this tutorial to scale their video players too.