Archive
You are currently browsing the Scott Morgan blog archives
for the year 2007.
By scott
In AS2 there were three mouse events that I used quite often. onDragOver was thrown when the mouse button is selected and the cursor was moved over a MovieClip. This was useful to detect if another object was being dragged over a given clip. onDragOut was the opposite, this event was thrown when the mouse button was selected and the cursor was dragged outside of a given clip. And lastly, onReleaseOutside was thrown when a clip was pressed and the user dragged off the clip before releasing the mouse button. Releasing the mouse outside is a common way for a user to bail out of selecting a button among other reasons.
If you look through the AS3 documentation you will notice there are no MouseEvent.DRAG_OVER, MouseEvent.DRAG_OUT, or MouseEvent.RELEASE_OUTSIDE events. This does not mean that this functionality is not available, it’s just not as obvious. The following code shows how to recreate this functionality in AS3.
var button:Sprite = new Sprite();
button.graphics.beginFill(0x000000, 1);
button.graphics.drawRect(50,50,200,100);
addChild(button);
button.buttonMode = true;
button.addEventListener(MouseEvent.MOUSE_DOWN, buttonPress);
button.addEventListener(MouseEvent.MOUSE_UP, buttonRelease);
button.addEventListener(MouseEvent.MOUSE_OVER, buttonOver);
button.addEventListener(MouseEvent.MOUSE_OUT, buttonOut);
function buttonPress(e:MouseEvent):void {
//the button is pressed, set a MOUSE_UP event on the button's parent stage
//to caputre the onReleaseOutside event.
button.parent.stage.addEventListener(MouseEvent.MOUSE_UP, buttonRelease);
}
function buttonRelease(e:MouseEvent):void {
//remove the parent stage event listener
button.parent.stage.removeEventListener(MouseEvent.MOUSE_UP, buttonRelease);
//if the events currentTarget doesn't equal the button we
//know the mouse was released outside.
if (e.currentTarget != button) {
trace('onReleasedOutside');
} else {
//the events currentTarget property equals the button instance therefore
//the mouse was released over the button.
trace('onRelease');
}
}
function buttonOver(e:MouseEvent):void {
if (e.buttonDown) {
//if the mouse button is selected when the cursor is
//over our button trigger the onDragOver functionality
trace('onDragOver');
} else {
//if the mouse button isn't selected trigger the standard
//onRollOver functionality
trace('onRollOver');
}
}
function buttonOut(e:MouseEvent):void {
if (e.buttonDown) {
//if the mouse button is selected when the cursor is
//moves off of our button trigger the onDragOut functionality
trace('onDragOut');
} else {
//if the mouse button isn't selected trigger the standard
//onRollOut functionality
trace('onRollOut');
}
}
So let me explain what is going on here.
To recreate the onDragOver and onDragOut functionality we use the buttonDown property of the MouseEvent. The buttonDown property is a Boolean value that indicates if the users mouse button is selected when the event was triggered. If this property is true we know the user is dragging over or dragging out of the button. If the buttonDown property is false we can trigger our standard rollOver or rollOut functionality.
Recreating the onReleaseOutside functionality is a trickier multi-step process. First we must set up a MouseEvent.MOUSE_DOWN event listener on the button. Secondly we set up a MouseEvent.MOUSE_UP event listener on the same button. Third, in the MouseEvent.MOUSE_DOWN event handler we set up another MouseEvent.MOUSE_UP event listener on the button’s stage that triggers the same event handler as the MouseEvent.MOUSE_UP event handler (buttonRelease) that is set on the button. And lastly we check the currentTarget property of the event passed into the MouseEvent.MOUSE_DOWN event handler to see if it equals the button or the button’s parent stage. If the currentTarget equals the button we know the mouse button was released over the button and we can trigger the onRelease functionality. If the currentTarget property isn’t the button we can assume the user released the mouse button outside of the button. Each situation is different, button.parent.stage may not work in your application, you may have to traverse up the display list, possibly right down to the root child (the Stage) to succesfully detect the onReleaseOutside event. Below I have updated the buttonDown method to show a simple way of traversing up the display list as far as you can go and setting the MouseEvent.MOUSE_UP on the root child.
function buttonPress(e:MouseEvent):void {
//the button is pressed, set a MOUSE_UP event on the root child in the display list
//to caputre the onReleaseOutside event.
var rootChild:DisplayObject = button;
while (rootChild.parent != null) {
rootChild = rootChild.parent;
}
rootChild.stage.addEventListener(MouseEvent.MOUSE_UP, buttonRelease);
}
Enjoy!
By scott
My previous team of Flash Developers at Critical Mass have recently released the start of a Flash API for the popular Google Maps API. You can read more about it here.
Currently AS2 is only supported, they mention an AS3 version will be available sometime in 2008, hopefully early 2008. I have heard from a few people that Google is about to release their own Flash API but until then we have Scott Ingalls and the entire Flash team at Critical Mass to thank.
By scott
Josh Tynjala and I were speaking today, well actually typing over IM. We were discussing a problem we both ran into recently, the fl.transitions.Tween class was not dispatching events consistently. Sometimes the TweenEvent.MOTION_FINISH event would be broadcasted, other times it would not. Sometimes our programmatic tweens were visually completing, other times they would not, in a few cases they wouldn’t even start. After a little debugging we realized that the AS3 Garbage Collection was killing the tweens. You may be asking why would the garbage collection be doing this? Let’s start out by looking at some example code.
var len:int = _model.getNumberOfItems();
for (var i:int = 0;i<len;i++)>
var clip:MovieClip = _scope.getChildByName('button'+i);
var myTween:Tween = new Tween(clip, 'alpha', Regular.easeOut, 0, 1, 0.5, true);
myTween.addEventListener(TweenEvent.MOTION_FINISH, buttonAnimationComplete);
}
Pretty simple code, in my AS2 days I used code similar to this a million times and never had a problem. All I am doing is looping through a set number of MovieClips and fading them up from 0 to 100%. Most times this worked fine, but a few times the tweens never completed and buttonAnimationComplete was not called. This is because the AS3 Garbage Collection was deleting the Tweens just like it should be. What? The tweens should be deleted? Yes.
Notice that I am storing the reference to my tween in a local variable that gets overridden in every iteration of the loop. This means there is no real reference to the object and when there is no reference to an object it is flagged for removal by the garbage collector. Since there is no real way to know when the garbage collection cycle is going to run the tweens were completing some of the time and other times were being thrown into the back of the big garbage truck that slowly creeps around your code.
The best way to ensure that this doesn’t happen is to store your reference in a class level member. I ended up storing all of my tween references in a class level array. Once all of the tweens were complete I cleared the array. Here is the updated code that I used, the entire class is not shown here, trust me your scroller will thank me.
private var _buttonTweens:Array = new Array();
private var _buttonTweenCompleteCount:int;
private function animateButtons():void {
var len:int = _model.getNumberOfItems();
for (var i:int = 0;i<len;i++)>
_buttonTweens.push(new Tween(clip, 'alpha', Regular.easeOut, 0, 1, 0.5, true));
_buttonTweens[_buttonTweens.length - 1].addEventListener(TweenEvent.MOTION_FINISH, buttonAnimationComplete);
}
}
private function buttonAnimationComplete(e:TweenEvent):void {
buttonTweenCompleteCount++
if (buttonTweenCompleteCount == _model.getNumberOfItems()) {
_buttonTweens = [];
dispatchEvent(new Event('buttonsReady'));
}
}
I am just using Tweens as an example, but you can run into this same issue with any object that has local references that are overridden. For example, I had a Timer event set to iterate every 100 milliseconds and it called loadAsset(), a method that did just what it says, it loaded an asset.
private function loadAsset():void {
_currentItemToLoad++;
var myLoader:Loader = new Loader();
myLoader.addEventListener(Event.COMPLETE, assetLoaded);
myLoader.load(new URLRequest('images/galleryImage' + currentItemToLoad + '.jpg'));
}
private function assetLoaded(e:Event):void {
var holder:Sprite = new Sprite();
holder.visible = false;
holder.addChild(e.target.content);
_scope.addChild(holder);
e.target.removeEventListener(Event.COMPLETE, assetLoaded);
}
Just like the Tween example above my Event.COMPLETE event was not firing all the time. This is because I was storing the reference to the Loader in a local variable that was overridden each time the loadAsset method was called. To get around this I stored all my references in a class level member (an object or an array work nicely).
Hopefully this helps you figure out why you are not seeing certain events in your code, it had me stumped for a bit. Now you know, and as G.I. Joe said “knowing is half the battle”.
By scott
First off, thank you Grant for releasing the source for your squeeze effect. Dirty code is better than no code. After looking at the source code I began to wonder how much better it would run in AS3 so I converted it. I didn’t clean up any of the code, just a straight conversion. It seems to run a little smoother. Next I am going to make it dynamic allowing you to load in an image passed in through flashVars. Maybe if I’m really bored I will set it up so you can load in multiple images from an xml file and build a gallery type app using this effect. Again, thanks Grant, as always, very cool work!
Download the rough AS3 source here.
By scott
I have seen a lot of posts lately with people asking how they can access variables and methods in an external swf that is loaded at runtime using AS3. This isn’t a difficult task, but it is much different than AS2/AS1 where you could just call directly into the loaded swf using the instance chain if you were in the same security sandbox.
First off, lets create our swf that wil be loaded in at runtime. Open up your favourite actionscript editor and create a new class with the following code.
package com.scottgmorgan {
import flash.external.ExternalInterface;
import flash.display.Sprite;
public class ExternalMovie extends Sprite {
public function ExternalMovie():void {
//nothing in our constructor right now.
}
public function alert(msg:String):void {
trace(msg);
ExternalInterface.call('alert', msg);
}
}
}
Now create a new FLA and set the above class as the document class. If you are not sure how to do this simply enter the class path (com.scottgmorgan.ExternalMovie) into the document class textfield found in the property panel. Lather, rinse, repeat, compile.
Next we will create the swf that will load our ExternalMovie swf we just created. Let’s jump back to our favourite actionscript editor and create a new class with the following code.
package com.scottgmorgan {
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.display.LoaderInfo;
import flash.display.Sprite;
public class SourceMovie extends Sprite {
public function SourceMovie():void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest('ExternalMovie.swf'));
}
private function onLoadComplete(e:Event):void {
var loaderInfo:LoaderInfo = e.target as LoaderInfo;
addChild(e.target.content);
var swf:Object = loaderInfo.content;
swf.alert('Hello World');
}
}
}
That’s it, LoaderInfo saves the day. LoaderInfo.content connects you with the document class of the externally loaded swf. Lets create a new FLA, assign the SourceMovie class as the document class and compile. Make sure the SourceMovie.swf and ExternalMovie.swf are in the same directory. The as files should be in /com/scottgmorgan/. Let’s compile the SourceMovie and you should see “Hello World” in the output window if you run the swf inside the IDE, if you run it from a browser you should see an alert dialog with “Hello World”.
Another option you have is to use the ApplicationDomain class. Using the ApplicationDomain class you can add the classes from the ExternalMovie to the SourceMovie’s ApplicationDomain. This is a great way to load in code libraries at runtime.
Lets pretend we have a large application with multiple levels of security, maybe we are creating a content management system and we need multiple permission levels. User A can only update content, User B can update content and update the site map, User C is an administrator and can do everything User A and B can do but can also access tracking information, edit user profiles, update permissions, etc. When User B logs in the application loads the site map code library (sitemapadmin.swf) and adds its classes to the main ApplicationDomain. When User C logs in the sitemapadmin.swf classes would be added to the main ApplicationDomain, for this user the application would also load the trackingadmin.swf, and useradmin.swf code libraries and add all the included classes to the main ApplicationDomain.
Let’s update our SourceMovie.as file and add the ExternalMovie class to SourceMovie’s ApplicationDomain.
package com.scottgmorgan {
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.system.ApplicationDomain;
public class SourceMovie extends Sprite {
public function SourceMovie():void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest('ExternalMovie.swf'));
}
private function onLoadComplete(e:Event):void {
ApplicationDomain.currentDomain.getDefinition("com.scottgmorgan.ExternalMovie");
var myExternalMovie:ExternalMovie = ExternalMovie(e.target.content);
myExternalMovie.alert('Hello World');
}
}
}
There you have it. One thing you will notice is you don’t have to add the loaded swf to the display list to access its classes. Hopefully you will be able to use these techniques in future projects.
By scott
Yay Leopard arrived on store shelves on Friday!! The upgrade takes forever but in true apple style they have created a very nice update to OSX. However, if you visit a site that is using Flash and the FileReference.upload() in any browser running in Leopard you will not be happy. Not sure if Apple will need to push out an update or if Adobe will. Let’s hope it is Apple, we all know how much fun it is detecting plug-in minor versions and an OS version just to circumvent certain functionality. As it stands the multi-uploader on Flickr isn’t working any more (more here). I found a few others discussing this issue too. And then there is this thread discussing all the bugs found in Leopard, don’t these people have anything better to do with their weekends
By scott
It amazes me that people don’t know about describeType(). If you’re lazy, or as some developers call it, efficient, you always don’t have time, or really don’t feel like digging through half assed completed documentation to see what parameters a classes constructor accepts. Sounds like the perfect time to combine forces, trace() meet flash.utils.describeType(). The describeType() method returns an xml dump that contains all the details you’d ever need to know about any object that is passed in as a parameter.For example, let’s pretend this is our first time working with Flash (of course you could be working in Flex too), lucky us, we never had to use AS2 or AS1, we only heard our cubicle neighbors groaning about spaghetti code, and hidden code attached to buttons. Since this is our first time using Flash we don’t even know what a MovieClip is or what methods are available to us. One simple line of code will solve our ignorance.
trace(flash.utils.describeType(flash.display.MovieClip));
This one little line of code returns this to us in our output window (shortened for your scroll wheels sake):
<type name="flash.display::MovieClip" base="Class" isDynamic="true" isFinal="true" isStatic="true"> <extendsClass type="Class"/> <extendsClass type="Object"/> <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/> <factory type="flash.display::MovieClip"> <extendsClass type="flash.display::Sprite"/> <extendsClass type="flash.display<::DisplayObjectContainer"/> <extendsClass type="flash.display::InteractiveObject"/> <extendsClass type="flash.display::DisplayObject"/> <extendsClass type="flash.events::EventDispatcher"/> <extendsClass type="Object"/> <implementsInterface type="flash.display::IBitmapDrawable"/> <implementsInterface type="flash.events::IEventDispatcher"/> <accessor name="scenes" access="readonly" type="Array" declaredBy="flash.display::MovieClip"/> <method name="stop" declaredBy="flash.display::MovieClip" returnType="void"/> <method name="nextScene" declaredBy="flash.display::MovieClip" returnType="void"/> <method name="play" declaredBy="flash.display::MovieClip" returnType="void"/> <accessor name="currentScene" access="readonly" type="flash.display::Scene" declaredBy="flash.display::MovieClip"/> <accessor name="currentFrame" access="readonly" type="int" declaredBy="flash.display::MovieClip"/> <accessor name="trackAsMenu" access="readwrite" type="Boolean" declaredBy="flash.display::MovieClip"/> <method name="gotoAndStop" declaredBy="flash.display::MovieClip" returnType="void"> <parameter index="1" type="Object" optional="false"/> <parameter index="2" type="String" optional="true"/> </method> <accessor name="currentLabels" access="readonly" type="Array" declaredBy="flash.display::MovieClip"/> <method name="willTrigger" declaredBy="flash.events::EventDispatcher" returnType="Boolean"> <parameter index="1" type="String" optional="false"/> </method> </factory></type>
The output window never looked so good. Methods, paramaters, superclasses, accessors, the amount of information that is returned is beautiful. I don’t know how many for in loops I wrote in AS2 just to try and figure out what methods were available in a custom class.One thing to note is that describeType() only shows public properties and methods, and will not show properties and methods that are private, package internal or in custom namespaces.If you have never looked at the flash.utils.* package, I suggest you do, there are some handy little utilities in there.
By scott
After 6 years with the same design it was time to retire my old website. Like any busy developer or company for that matter personal projects always get pushed a side. I figured since I am starting new in L.A. that I should finally get around to updating my Blog. And here it is. I rolled my own CMS for my previous site and blog, it worked great, but it was dated. So now, like many, I have switched to WordPress.
I will soon have my portfolio back up, this time I am going to try and do something a little bit cooler with it, and since I am a Flash/Flex developer I will probably do something using the aforementioned technologies. For those who can’t wait to see what I’ve worked on the old portfolio can be viewed here. Stay tuned and let me know what you think of the new site.
Props to the designers of this theme N.Design Studio and MangoOrangeâ„¢, I haven’t set up all the 301s for the old content, so if you have bookmarks you may see the old design rearing it’s ugly <head>.
By scott
I posted a question on the Papervision3D forum earlier this week and no one has posted an answer yet. It seems like a simple question but I have tried every possible method and property known to man and I can’t find a solution. The question I posted was:
“I have created a MovieMaterial and I placed it in a panel. By default the MovieMaterial is 106×76 and I have set animated = true on it. When my carousel rotates to a specific plane a swf is loaded into the MovieMaterial and I call gotoAndPlay(’in’) on the loaded swf. The animation plays but since the animation goes larger than 106×76 it is cropped. I am trying to get the effect of the animation busting out of the panel/material. How can I turn off the masking on the panel, or is the material masked? I have set allowAutoResize to true, and have tried almost every property and method available, what am I missing? “
Does anyone have an answer out there? Please! I love that I am able to work with Papervision again, hopefully soon I will be able to post some of the cool stuff I am working on. I thank you in advance if you know the answer.
By scott
Personally I think they are just psychological. A technical placebo if you will. I noticed tonight when I was shopping in “beautiful downtown Burbank” that they have updated a few of the pedestrian crossing buttons with fancy blinking buttons that beep when you push them. I still don’t think they do anything, I think the beep and blink is another psychological trick, In your mind, when you push a button, you expect something to happen, the old plunger style buttons didn’t do anything and left you wondering if anything was going to happen. This is probably my most bizarre posting just wondering if anyone has any proof that these things actually change the light quicker. Next week we’ll discuss the close door button on an elevator