Custom Mouse Cursor for ActionScript 3

May 30, 2009 · Posted in advice, code · 7 Comments 

I threw together an FLA for someone to show them the different ways to handle custom mouse cursors in ActionScript 3 (AS3). I figured I should start posting more of my code for people since I use other people’s blogs all the time to figure out my own problems.

I’m mostly coding up stuff for Flash, Flex, Augmented Reality, iPhone, PHP, and CSS/HTML right now so hopefully I’ll have the time to post more snippets of things I’ve figured out trying to get all this technology to work.

This movie requires Flash Player 9

Direct Access to the X and Y Mouse Coordinates

The first method allows us a finer control over the movement of the cursor by directly changing the X and Y coordinates of the cursor movieclip with the X and Y coordinates of the mouse. If we wanted to restrict the movement to say, a specific Y coordinate (as we’ll see in the next example), we can do that just as easily by keeping the target_mc.y a constant when we update it’s X coordinate.

// Set up our listeners for each of our mouse actions
boundary_mc.addEventListener( MouseEvent.MOUSE_OVER,
onMouseOver );
boundary_mc.addEventListener( MouseEvent.MOUSE_OUT,
onMouseOut );

// Ignores all sub movie clips and treats them as the parent
// -- otherwise events will be triggered on the child
// movieclips and not our main clip
target_mc.mouseChildren = false;
boundary_mc.mouseChildren = false;

// Ignore all the mouse events for the cursor
// -- otherwise MOUSE_OUT will trigger since mouse is
// now on target_mc instead of boundary_mc
target_mc.mouseEnabled = false;

///////////////////////////////////////////////////////////////
// ON MOUSE OVER /////////////////////////////////////////////
/////////////////////////////////////////////////////////////
// When the mouse rolls over onto our red boundary, hide
// mouse/show cursor
//
function onMouseOver( theEvent:MouseEvent ):void
{
// Swap the mouse for the cursor
Mouse.hide(); // hide mouse cursor
target_mc.visible = true;

// Add a listener to handle the moving of the mouse
// We add the listener here because otherwise the onMouseMove
// would execute whenever the mouse moved, whether it was
// on our boundary or not. Not a big deal, but better for
// efficiency
boundary_mc.addEventListener( MouseEvent.MOUSE_MOVE,
onMouseMotion );

// Handle the movement now so we don't have to wait for a
// mouse movement to reposition the cursor
onMouseMotion(null);
}

///////////////////////////////////////////////////////////////
// ON MOUSE MOTION ///////////////////////////////////////////
/////////////////////////////////////////////////////////////
// When the mouse moves, we also want to move our target to follow
// the mouse
//
function onMouseMotion( theEvent:MouseEvent ):void
{
// Reposition our custom cursor
target_mc.y = stage.mouseY;
target_mc.x = stage.mouseX;
}

///////////////////////////////////////////////////////////////
// ON MOUSE OUT //////////////////////////////////////////////
/////////////////////////////////////////////////////////////
// When mouse goes outside our red boundary, show default mouse,
// and hide our custom one
//
function onMouseOut( theEvent:MouseEvent ):void
{
// Swap the cursor for the mouse
Mouse.show();
target_mc.visible = false;

// Stop listening for mouse movement
boundary_mc.removeEventListener( MouseEvent.MOUSE_MOVE,
onMouseMotion );
}

The Drag and Drop Method

Our next method uses the drag function built into Flash. With this function you can drag a movieclip around the stage with the mouse automatically. This restricts some of our freedom, but gives us some very easy coding to control the mouse cursor to specific regions. In this case we simply restrict the motion of the cursor along a rectangle of 0 height (so it won’t move along the Y axis) and with a width as wide as our boundary box. This feature has its advantages, and I merely include it to demonstrate how to use it in the context of a custom mouse cursor.

// Set up our listeners for each of our mouse actions
boundary2_mc.addEventListener( MouseEvent.MOUSE_OVER,
onMouseOver2 );
boundary2_mc.addEventListener( MouseEvent.MOUSE_OUT,
onMouseOut2 );

// Ingores all sub movie clips and treats them as the parent
// -- otherwise events will be triggered on the child
// movieclips and not our main clip
target_mc.mouseChildren = false;
boundary2_mc.mouseChildren = false;

// Ignore all the mouse events for the cursor
// -- otherwise MOUSE_OUT will trigger since mouse is
// now on target_mc instead of boundary_mc
target_mc.mouseEnabled = false;

// Calculate the the vertical center of our boundary
// so that the cursor can move along that line
var verticalCenter_num = boundary2_mc.y + (boundary2_mc.height / 2);

///////////////////////////////////////////////////////////////
// ON MOUSE OVER 2 ///////////////////////////////////////////
/////////////////////////////////////////////////////////////
// Same as our previous one, but set for another boundary
//
function onMouseOver2( theEvent:MouseEvent ):void
{
// Swap the mouse for the cursor
Mouse.hide();
target_mc.visible = true;

// Add a listener to handle the moving of the mouse
// We add the listener here because otherwise the
// onMouseMove would execute whenever the
// mouse moved, whether it was on our boundary
// or not. Not a big deal, but better for efficiency
boundary2_mc.addEventListener( MouseEvent.MOUSE_MOVE,
onMouseMotion2 );

// Handle the movement now so we don't have to wait
// for a mouse movement to reposition the cursor
target_mc.y = verticalCenter_num;
target_mc.x = stage.mouseX;
}

///////////////////////////////////////////////////////////////
// ON MOUSE MOTION 2 /////////////////////////////////////////
/////////////////////////////////////////////////////////////
// Restricts the movement of our custom cursor along the X axis
//
function onMouseMotion2( theEvent:MouseEvent ):void
{
// Calculate the the vertical center of our boundary
// so that the cursor can move along that line
var verticalCenter_num = boundary2_mc.y +
(boundary2_mc.height / 2);

// We use startDrag() here since it's easier to restrict
// the cursor's boundaryusing a Rectangle object.
// boundary2_mc.x: Start from the x position of our
// boundary verticalCenter_num: Start from the
// vertical center as our y coordinate
// boundary2_mc.width: Allow the cursor to move
// the length of the boundary's width (starting from x)
// 0: Don't allow the cursor to move up or down
target_mc.startDrag( true, new Rectangle(
boundary2_mc.x,
verticalCenter_num,
boundary2_mc.width,
0)
);
}

///////////////////////////////////////////////////////////////
// ON MOUSE OUT 2 ////////////////////////////////////////////
/////////////////////////////////////////////////////////////
// When mouse goes outside our gray boundary, show default mouse,
// hide custom mouse and stop drag
//
function onMouseOut2( theEvent:MouseEvent ):void
{
// Swap the cursor for the mouse
Mouse.show();
target_mc.visible = false;

// Stop our drag
target_mc.stopDrag();

// Stop listening for mouse movement
boundary2_mc.removeEventListener( MouseEvent.MOUSE_MOVE,
onMouseMotion2 );
}

I hope the code is commented well enough for you to understand what’s going on. It should be fairly self explanatory, but if not, feel free to ask.

Sample Code

Below is the source code for the SWF used at the top of the post:

Download the Custom Cursors sample code.

Did you find this useful?

If you found this page useful, please consider helping us out by checking out the links on the right. Thanks!

Fixing What’s Not Broken: A User-Centric Perspective

January 29, 2009 · Posted in advice, projects, work · Comment 

The saying, “If it ain’t broke, don’t fix it,” is not a very good motto, especially when there’s progress to be made. When you’re working with old and tired technology it really drags down your capabilities and your ability to move forward. The need to keep around legacy solutions only holds back your clients from realizing your full potential as a company.

Their Failure is My Failure

Being the Technical Director at my company means that I get to pull out all the stops when it comes to investing in our technical capability as a company. But it also means that I am responsible for those technological failures as well.

Coming from the world of Human Computer Interaction (HCI), the user is never wrong or stupid, but instead it is your design that is wrong or inadequate and the sooner you realize that, the sooner you can move on to building better, more functional and friendly software.

The same goes for my developers. They are forced to use the solutions I provide for them and if they have problems with it, break it, or can’t get it to work, that’s my failure to design a well-working platform for them to develop on. Sure, I can always fix the problem for them in a few minutes and show them where they were misled because I understand how all the pieces fit, but I’m the one that made it, so of course I understand it. I could dismiss their problems as them not knowing how the code works or that certain things need to be placed in particular spots for things to work properly, but that’s not their job. Their job is to develop really great pieces of work and it’s only a difference of mentality on how they should do their jobs better and easier.

On the one hand, I could produce documents and try to explain how everything works and occasionally field questions on how certain things work. The alternative is to pay attention to the problems they’re running into and resolve it in a way that’s best for them. This may require a higher initial investment, but it’s an investment that’ll yield higher returns in the long run, not to mention happier developers.

It’s Not Them, Stupid, It’s You

We’re quick to fault others for something that we ourselves may understand quite well, but with the wealth of diversity in this world, no one can know exactly the same things you do. I work with really talented people and I can’t blame them for not knowing things I think are common sense because I know they know things that I couldn’t even begin to grasp. So instead of trying to be superior to people or trying to absolve yourself from blame, take the stance that the end-user is always right, be it your co-workers, your clients or your customers. Remember, you’re not trying to compete with them, you’re trying to work with them.

Don’t Fix It, Improve It

Take something in your line of work that is tired and old and revamp it. If your software is coded in ActionScript 2, upgrade it to 3. If your website is made with just straight up HTML, refactor it to include CSS and some JavaScript. If your invoices are still written in the Courier font, hire a designer to make them less intimidating (unless that’s the point, in which case your business model is flawed if you’re constantly trying to convince your customers to pay up). I know it might not seem broken and may seem to work fine, but by improving on what you already have, it allows you to evolve and be a leader rather than having tired old things hanging around and weighing you down.