Movable DOM Node Widget

One of my favorite pastimes is playing and collecting video games, which combined with another of my passions, JavaScript, drives me to figure out ways to port simple gaming engines into JavaScript. JavaScript is not the best language for writing such engines (at least not in the browser), so I do not spend much time on this. However, I had a little time and revisited an old project, replacing the ancient coding with some simple YUI calls. When I wrote this, I was thinking of the original Dragon Warrior game, where you had a character that moved around the screen.

Think of the black rectangle as the character. Using YUI animation utility and a custom Object Movable, I listen to arrow keydown events and trigger animations on Movable. Because YUI manages duplicate animations on the same style of an Element (preventing it, until the previous animation completes), this was much simpler than code I had previously written. Also, the YUI animation code allows for diagonal movement if you press two perpendicular arrow keys within the half-second animation period. Try moving the black box around the screen on the Movable Object Test Page. Here is the code for the Movable Object:

Example 1: Movable Class

Core.Widget.Movable = function(id, useAnim) {

    // Local Variables

    var F = function() {},
        node = $(id),
        that = null,
        anim = null;

    // shortcut for viewport size
    var getWSize = Core.Client.getViewportSize;

    
    // Public Variables

    F.prototype = {

        /**
         * Move the object down.
         *
         * @method down
         * @public
         */
        down: function() {
            var r = Dom.getRegion(node),
                w = r.bottom - r.top,
                o = r.bottom,
                s = getWSize();

            // prevent exceeding viewport height
            if (s.y < o + w) {o = s.y - w;}

            if (useAnim) {
                anim = new YAHOO.util.Anim(node, {top: {to: o}}, 1, YAHOO.util.Easing.easeOut);
                anim.animate();
            }
            else {
                Dom.setStyle(node, top, o + px);
            }
        },

        /**
         * Move the object left.
         *
         * @method left
         * @public
         */
        left: function() {
            var r = Dom.getRegion(node),
                w = r.right - r.left,
                o = r.left - w;

            // prevent exceeding viewport width
            if (0 > o - w) {o = 0;}

            if (useAnim) {
                anim = new YAHOO.util.Anim(node, {left: {to: o}}, 1, YAHOO.util.Easing.easeOut);
                anim.animate();
            }
            else {
                Dom.setStyle(node, left, o + px);
            }
        },

        /**
         * Move the object right.
         *
         * @method right
         * @public
         */
        right: function() {
            var r = Dom.getRegion(node),
                w = r.right - r.left,
                o = r.right,
                s = getWSize();

            // prevent exceeding viewport width
            if (s.x < o + w) {o = s.x - w;}

            if (useAnim) {
                anim = new YAHOO.util.Anim(node, {left: {to: o}}, 1, YAHOO.util.Easing.easeOut);
                anim.animate();
            }
            else {
                Dom.setStyle(node, left, o + px);
            }
        },

        /**
         * Move the object up.
         *
         * @method up
         * @public
         */
        up: function() {
            var r = Dom.getRegion(node),
                w = r.bottom - r.top,
                o = r.top - w;

            // prevent exceeding viewport height
            if (0 > o - w) {o = 0;}

            if (useAnim) {
                anim = new YAHOO.util.Anim(node, {top: {to: o}}, 1, YAHOO.util.Easing.easeOut);
                anim.animate();
            }
            else {
                Dom.setStyle(node, top, o + px);
            }
        }
    };

    // Scoped Operations

    that = new F();
    return that;
};

The Movable class is designed to work with any DOM node that is absolutely positioned and smaller than the viewport. Simply pass the DOM node as the first parameter and whether you want the movements to be animated as the second. There is any number of reasons that you may need an Element on a page that can move up and down, based on its size, so you neednt only think of it as part of a gaming engine. Keep in mind that I have used shorthand notation for YAHOO.util.Dom (Dom), and YAHOO.util.Dom.get ($). This widget also requires that you include core.js.

Lastly, if you want to have it animate on the page, like in my example, you will need to use YUI Event package to listen to all keydown events on the document. Here is the code that I used:

Example 2: Document Keydown Listener

Event.addListener(document, keydown, function(e) {
	var test = Core.Widget.Movable(obj, true);
	
	switch (Event.getCharCode(e)) {
		case 37: // left arrow
			test.left();
			break;
			
		case 38: // up arrow
			test.up();
			break;
			
		case 39: // right arrow
			test.right();
			break;
			
		case 40: // down arrow
			test.down();
			break;
	}
});

Again, I have shorthanded the YAHOO.util.Event package to just Event. This code simply listens for all keydown events, ignoring all but the arrow keys. When any key is fired, it calls the appropriate method on Movable, which then handles the movement.

Now, the next step of the engine is to have the Object move over something, such as a map or a game board, firing custom events that the engine listens to. My next article will show how the Movable&rsquot; object can be used to build a simple game.