64x64 icon dark hosting
Choose a hosting plan here and get a free year's subscription to Tuts+ (worth $180).
Advertisement

Throw Objects by Creating a PanAndThrow Class

by
Gift

Start a hosting plan from $3.92/mo and get a free year on Tuts+ (normally $180)

In this tutorial we will be mocking up and finishing a pan and throw class that will allow us to add this effect to any element we want. To accomplish this, we will create a image viewer - but not your average viewer. Here we'll have zooming, throwing, panning.. Almost sounds like a ninja app, huh?


Step 1: Introduction

The Pan and Throw Class will allow you to add the pan and throw functionality to any ActionScript object you want. Although this tutorial is specifically for Flex, the class itself can be used anywhere ActionScript is. I had seen this effect on several websites, then in Photoshop CS4 and decided that I wanted this on my projects too.

There are many applications for this effect; the one that we are going to be using for this tutorial is an image viewer that lets you zoom in and out of am image and change the friction that the throw effect uses. However, this tutorial isn't really about the image viewer, it is about making a pan and throw class. So let's get started with it. Open up your favorite Flex editor and get a project going; for information on doing this in Flex Builder see the Adobe LiveDocs. Once your project is created, open the MXML file. We need to add some code to this before we create our class.


Step 2: Our MXML

Since this isn't the major portion of the tutorial I am not going to spend much time here. If you have any questions about this section that aren't covered, you can ask in the comments below. Firstly, here are the MXML objects to put in the application:

You'll notice the four functions called in the tags: init(), changeDecay(), smoothImage() and zoom(). We need to write up those functions. This is the code between the <mx:script> tags:

Once you have your MXML you need to create a folder called "classes" in the same folder as your MXML file. (If using Flash, the folder needs to be in the same dir as your FLA file.) This is our classes package and is where the PanAndThrow.as file will go. In Flex Builder, create a new class, put it in the classes package, and call it PanAndThrow; this will create your class - default style.


Step 3: The Makings of a Class

Here's our basic PanAndThrow class. Save it as PanAndThrow.as in your new "classes" folder.

Which variables and functions do we need in our PanAndThrow class? To get that, you can ask yourself "what does my class need to do, what does it need to know, and what does it need to be able to do it?" So let's create some pseudo-code.

Quick Note

When I first developed this class I did put everything in the constructor, but that lead to a problem when I created the start and stop methods because of scope. I couldn't instantiate this class on a global scope with all the information required. I therefore made an init() function, so the instance could be started and stopped from outside the class.


Step 4: Our Pseudo-Code

"Pseudo-code" just means fake code, that we can use to help ourselves think about what real code we'll need.

Now that we have some pseudo-code we can start building the class. Let's start with the init() function. This will also bring us into one of the principles of Object Oriented Programming called encapsulation, which deals with the access of pieces of the code.

This code should go in the PanAndThrow class we've just started. (Not sure where? Check out the Document Class Quick Tip.)

Just a couple of things I want to point out. In the function for init I have set a few of the arguments to be equal to a value. That means I'm giving them a default value, thus making them optional. When setting default values for arguments of a function, they have to be the last parameters - you can't have a required variable after an optional one. The reason I added default variables is to make the call shorter if we use the default settings. I can call PanAndThrow(mover, eventer); and be done, instead of PanAndThrow(mover, enventer, decayer, yVal, ... ) and so on.

Have you ever wondered what the "private" or "public" in front of functions and variables means? That is the exposure of the object. A "public" object can be accessed by any other class; a "private" object can only be seen by the other members of this class; a "protected" object is hidden from everything except classes which are in the same package.

We want to be able to change the decay from our MXML so we need a public hook to get to our private variable; this is where getter and setter functions come in:

That's a "getter" function. It means that, to outside classes, it looks like the PanAndThrow class has a public variable called "decay". When they try to access it, we will return to them the value of our (private) originalDecay variable.

Setter functions are almost the same, but allows the outside classes to change the value of our "fake" public variable:

These are useful because you can put logic into a setter to constrain what comes into your private var. For instance, if you put a Number in the MXML tag for a box you will get a set height; if you put a % (making the number a string) you will get a percentage height. That is built into the code for the box's height setter. Now that we have our getter and setter you can access the decay variable like this from outside the class:


Step 5: Start, Listen, Stop

We have our class, some local variables and an init() function. Let's do something now. At the end of the init() function we called "start();" so let's make the start function. Mostly it's just a bunch of listeners:

The stop() function is almost the same, but we are removing the listeners.

Now we can listen to what is going on, let's go through each of these listener functions.


Step 6: MouseEvent.MOUSE_DOWN

We are going to be looking at the handleOverTarget event handler.

This function will be called when there is a MOUSE_DOWN event on either the event object or the target object. It is very important to note that if I put a listener on a parent object, the handler will even be called when the event occurs on a child. In this case my target object is a child of the event object. When I click on the target object this method will be called twice: first for the mouse down on the child then second for the mouse down on the parent. That is really important for this because we are going to be deciding whether the mouse down will be able to move our target object so we really need to be able to know whether that mouse down was on the child or not.

The first statement is pretty straightforward: set our class variable buttonDown to true.

The next two are pretty easy also, except I have introduced a couple new variables that will need to be put in our class variable list: MousePrevX, MousePrevY, arMousePrevX, arMousePrevY, MouseCurrX and MouseCurrY. These will be used a lot in the drag and pan functions, so I will wait to talk about them till then.

The if statement checks to see whether the object clicked is the target object. Remember, TargetClick was set to the argument we passed to init(), OnlyMoveOnTargetClick; if this is false we want to treat every child object as the target object when clicked. That's why we have the "|| !TargetClick" check.

That's the easy part. The next part is a little trickier.

The e.currentTarget returns the object that triggered the event. The e.target will return the object that was the actual target. So I could say this, right?

That is simple enough, but it's wrong. What if my target object has children? Then my e.currentTarget may be the targetObject but the e.target is targetObject's child and will not match. We want this to move even if we are mousing down on a child object.

So here comes String.search to the rescue. If our currentTarget is not our targetObject, we use an "else if" to see if we can find our target object in the target. e.target.toString() will produce something like this: "application2.eventobject3.targetobject2.targetchild4" for our target object where targetObject.toString() will produce something like this "application2.eventobject3.targetobject2" all I need to do to find out if our target is a child of our targetObject is by this:

If there is a match it will return the first index of the match, or if there is not a match it will return a -1, so we can just see if it is greater than -1 and viola, we have found if the object being clicked on is a child of our targetObject.

(We could check the children or parent(s) of the object via the getChildAt() function and parent property, but this is a neat alternative.)


Step 7: TimerOut and Pan

The timer function is pretty easy too, especially since we have done this before. Well, almost done this before. When we have dragged around our little targetObject a bit and decide we don't want to let it go, we just love it too much, and abruptly stop the mouse, what would happen if you let go of the mouse button at that point? well, what do you think would happen? I am not going to answer that for you, I am just going to help you with the code to keep it from happening. In the final code, comment these three lines out. This should look really familiar, we just used this in the button down handler, except for one variable, MouseDragged. We are going to use that when we call our other function:

So, if you are asking why we need this timer event, you probably didn't try and take it out to see what was happening. So do that.

This next function is one of our main functions; it is the pan function. There is a lot involved so let's dive into our pseudo-code:

I admit this is a little more psuedo-y than the last; that is for two reasons: one, you don't know what is coming, and two, I am just really excited to get to the code:

And now we are panning.


Step 8: Throw, It, Out, Repeater!

This is the second big function and with this we will have our class built! Ready to pan and throw any object you see fit! There are two functions that we need to address first: throwIt(), which we set as a handler to the MOUSE_UP event, and throwItOut(), which we set as a handler to the MOUSE_OUT event.

These two functions are almost the same (after all, they are doing the same thing just at different times). In them we set the buttonDown to false, because this is a mouse up event, and check to see if the mouse was dragged, using either MouseDragged (which we set in the last function) or by checking "e.relatedObject"; the object that the mouse just moved out of.

If it was dragged we add another listener. The ENTER_FRAME event is a really cool one. This is the basis of our animation; every time we enter a new frame the throw() function will be run. That is what allows us to simulate a mouse drag after release (remember the arMousePrevX,Y variable? That is what it is for). And that is all the throw is really doing, simulating a mouse drag, without a mouse of course. So we have pretty much already got the function we need, except we need to replace the calls to the current mouse position to our artificial mouse position.

Almost got a little ahead of myself there. So with these two event functions, throwIt and throwItOut, they are doing the same thing but that if in the second function is worth mentioning. I struggled a while trying to get this functionality, until I looked at the event a little closer. The problem was trying to get the target object to act as though I let go of the button when the cursor left the event object. Go ahead, try and do this without the e.relatedObject. I almost had it a few times, but couldn't get it right. What the e.relatedObject does is finds what object you are on, after the event is called. That is why it is soooo cool. When our cursor leaves the movie altogether, it returns a null, otherwise it will return the object you are on, so we can check to see if e.relatedObject is null or is a parent to the eventObject. That produces the correct action we are looking for.

In the above functions we set up calls to theRepeater(). This will be the throw function, and remember it will be called every time we enter a new frame. Let's step through this line by line:

And with that, our class is finished.


Step 9: The Completed Class Code

You can grab the completed code from the Source zip, linked at the top of the tutorial. It's in the PanAndThrow.as class.


Step 10: Do Something With It

To do something with this we need to go back to the MXML and add a few lines of code. Add our declaration in the global variable section, fill in the decay method and call our pan and throw init() function. With all that added, here is the complete MXML file:

Conclusion

Now you have a working pan and throw class! I hope you are as excited as I am. There is a lot here and I hope I was able to cover everything and not make this tutorial too long.

I hope you liked this tutorial, thanks for reading! Please post in the comments if you have any questions.

Advertisement