JavaScript Animation that Works (Part 4 of 4)

In the first part of this series, we introduced the idea of using spriting as an easy, cross-browser way of having interactive animation for the web. In the second part, we got some animation working, and in the third we cleaned up our code and made it ready for the web.

Introduction

Now, in our final part today, we will walk through setting up event handlers so that instead of responding to clicked buttons, our robots will follow the mouse around the screen. In the process, we will also talk about making the code cross-browser friendly and touch screen enabled.

If you take a look at our code from last time, you will see that while the code runs well (and with multiple robots), there isn't a very easy way to tell the code to run.

Event Handlers

Event handlers are commands that tell certain code to run when certain events are triggered. For example, you could have my_function() run whenever a user clicks on your div with the id 'my_div'. Or, you could have my_other_function() run whenever a user moves their mouse over 'my_other_div'.

In theory, this is a pretty simple and straightforward idea. Unfortunately, once you start getting different browsers involved, this can get a bit confusing. In an ideal world, every web browser would interpret the same code and HTML in the same way, and developers would write code one time and it would work the same for every user. In the real world, different browsers may have completely different commands to do the same thing (*cough* *cough* Internet Explorer), and so sometimes trying to get a single piece of code to run the same on all browsers can feel like herding cats. Recently, the situation has been getting much better, as Chrome, Firefox, Safari, and Opera all respond very similarly to code, Internet Explorer 9 and 10 have become much more in line with standards than earlier versions, and almost no one uses Internet Explorer 7 or 6 anymore. So, for our code, we will be getting event handlers to work for both modern browsers and Internet Explorer 8.

As a side note, this is a case where it really pays to use a robust JavaScript library, such as jQuery. jQuery does all the work for you in cross-browser testing, so you will only need to enter one command and the jQuery library will translate it for each browser behind the scenes. Additionally, many of the commands in jQuery are much more intuitive and simpler than the core JavaScript as well.

But, since I am stubborn, and since this is a learning opportunity, we are going to continue on the hard way and do all of this solely with JavaScript and no dependencies!

Page Interaction

So, our first step will be to decide how exactly we want to interact with the page. When I move my mouse over the stage area, I want all of the robots to run towards the mouse. When they reach the mouse, or if the mouse is directly above them, I want them to stop running. If the mouse crosses over them, I want them to jump. And finally, when the mouse leaves the stage area, I want them to stop running. We will start with attaching these events inside the RobotMaker function:

So, in the above lines, we have said that whenever the user moves the mouse inside the stage element, we will trigger a function called stage_mousemove_listener() (notice we do not include the parentheses in the command). Similarly, when the user moves the mouse over the robot element, it triggers robot_mouseover_listener(), and when the user moves the mouse outside of the stage, it triggers stage_mouseout_listener().

Unfortunately, as we mentioned before, Internet Explorer 8 and below has a (similar but) different command to do the same thing, so we will need to test to know which command the user's browser will understand and do that method.

You may notice that the format of the commands is very similar, but has some major differences - one says 'addEventListener' while the other says 'attachEvent'. One says 'mousemove' while the other says 'onmousemove'. One requires a third parameter, while the other only uses two. Mixing any of these up will cause the command to not run. These are the kinds of things that will make you want to bang your head against the wall. Unfortunately, this isn't the end of the extra coding we will need to do for cross-browser capability.

Listening Functions

Next, we are going to write the listening functions. We will start with the function that is triggered when the user mouses over the stage. Since this is a mousemove listener, this function will trigger every time the mouse is moved inside the stage area (meaning it will trigger several times a second while the mouse is moving). This function will need to compare the location of the robot with the location of the mouse, and make the robot behave accordingly. Each time the function is triggered, it will check if the robot needs to continue running the same direction or change behaviors. So, it will need to be something like this:

So, in the function above, once we are able to find mouseX, we compare it to where the robot is and trigger or stop the different running functions as needed. Unfortunately, finding mouseX is a bit tricky, since mouse position is another thing that different browsers do differently. In lieu of (more) complicated and long-winded explanations, here is the cross-browser method for finding mouseX, as inspired from the excellent Quirksmode blog (which is a great source for more advanced JavaScript studying).

We have an argument called e in the function, even though we don't pass it anything. Since this is an event listener, we can have an automatic variable called e that stores event information like mouse data. But because different browsers store it differently, we have to add a lot of extra steps.

We finally find mouseX by finding posX (which is the x-position of the mouse on the page) and subtracting how far the stage is from the far left of the page (stored in stageOffset.xpos). This gives us how far from the left edge of the stage the mouse is, which we can directly compare with robot.offsetLeft. Since the stage could be located differently around the page depending on the layout, we will also need to find the exact pixel offset of the stage for the function to be accurate, and store that information in stageOffset. Fortunately there is a neat trick we can use to find an element's absolute offset with this function from Vishal Astik's blog.

So now that we have written the mousemove listener, the others will be much easier. For the robot mouseover listener, we only need to check if the robot is already jumping, and if not, stop the run timer and make it jump.

The mouseout listener is also pretty simple. We just need to reset some of our variables we are using to track the robot, and if the robot isn't jumping, return the robot to the standing sprite.

Animation Functions

The functions that animate the running and jumping motions haven't changed much this time. We have just added the tracking variable running_dir, taken out the statement that checks if the robot is about to hit the wall (since this is redundant with our mouseout function), and add a bit of code to the jump function that checks again if the robot should start running if the mouse is within the stage after it lands from a jump. Here's the final code (quite large):

So, now, we have our rewritten functions that work great across all browsers ... unless those browsers have touch input. We still have a bit more to go to make our robots run on everything. Since touch screens behave a bit differently, we will need to do some extra coding on our event listeners.

Supporting Touch Screens

We need to make some new rules for touch screens: If the screen is touched anywhere in the stage, the robot will run to that spot until the finger is lifted. If the user touches the robot, the robot will jump. First of all, we will add some extra touch event handlers to our earlier function, and we are going to write the code in such a way that it will run automatically whenever the RobotMaster function is called.

We won't have to worry about the touch listeners being in the Internet Explorer 8 format, and if any device doesn't have touch support it will ignore the listeners. Now we will need to update the stage_mousemove_listener() function to behave differently if the browser has touch capability.

You might notice that we no longer have any "doors" in our RobotMaker function, but since we are calling all of our code with event handlers that we are assigning inside RobotMaker, we no longer need them! For both our stage, and our characters, we will want to add a bit of CSS specially for touch devices so it will not try to cut and paste any images when a user holds down a finger on them.

And finally, we will declare all of our robots at the bottom of the page, using the same format as our event handler function to have the code run automatically when the page loads - this method also prevents these robot objects from being global variables, so the only global variable we have in this entire script is the RobotMaker() function.

Please checkout the final result in all of its glory!

Conclusion

I highly encourage you to study the entire (and fully commented!) code, and you can download all four robot sprites here as well.

Happy animating!

Tags:

Comments

Related Articles