2
\$\begingroup\$

This is just a simple script for testing sprite sheet animations. I'm trying to improve my code style, so I'm curious about suggestions anyone might have about best practices or readability.

<html>
    <head>
        <script>
            //just a shim to provide requestAnimationFrame support in different browsers
            if (!window.requestAnimationFrame) {
              window.requestAnimationFrame = (window.webkitRequestAnimationFrame ||
                                              window.mozRequestAnimationFrame ||
                                              window.msRequestAnimationFrame ||
                                              window.oRequestAnimationFrame ||
                                              function (callback) {
                                                return window.setTimeout(callback, 17 /*~ 1000/60*/);
                                              });
            }

            if (!window.cancelAnimationFrame) {
              window.cancelAnimationFrame = (window.cancelRequestAnimationFrame ||
                                             window.webkitCancelAnimationFrame || window.webkitCancelRequestAnimationFrame ||
                                             window.mozCancelAnimationFrame || window.mozCancelRequestAnimationFrame ||
                                             window.msCancelAnimationFrame || window.msCancelRequestAnimationFrame ||
                                             window.oCancelAnimationFrame || window.oCancelRequestAnimationFrame ||
                                             window.clearTimeout);
            }

            //loads when the body element has loaded.
            var init = function() {

                //the actual event loop. Runs inside of the init() closure.
                var animate = function() {
                    (function animloop(){
                        //clear the canvas
                        canvas.width = canvas.width;

                        //increment the tick variable.
                        tick++;

                        //if it's greater than five, reset to zero and go to the next tile
                        if (tick > 5) { 
                            tick = 0;

                            //check to see if currently viewing the last tile in the sequence. If so, reset to the first, otherwise go the the net one.
                            if (currentTile > tiles - 2) {
                                currentTile = 0;
                            } else {
                                currentTile++
                            }
                        }

                        //pull the current tile out of the sprite sheet and draw it to the canvas.
                        ctx.drawImage(img, currentTile * tileWidth, 0, tileWidth, tileHeight, 0, 0, tileWidth, tileHeight);

                        //let the browser decide when to call the event loop again
                        window.requestAnimationFrame(animloop);
                    })();
                };

                //get a reference to the canvas element
                var canvas = document.getElementById('canvas');

                //same for the canvas context
                var ctx = canvas.getContext('2d');

                //and index that refers to the tile currently selected
                var currentTile = 0;

                //used in the event loop to slow down the animation
                var tick = 0;

                //*************************************
                //change this next value depending on the number of tiles in the animation
                var tiles = 5;

                //used to select the tile currently being drawn from sprite sheet
                var tileWidth = null;

                //same
                var tileHeight = null;

                //new image object
                img = new Image();

                //called when the image loads
                img.onload = function() {

                    //the sprite sheet should be composed of tiles of equal length and the animation should be centered on each tile.
                    tileWidth = img.width / tiles;

                    //the script only handles sprite sheets that are one dimensional. 
                    //ie, the animation should only run along the x axis, so the animation will always be the same height as the image itself.
                    tileHeight = img.height;

                    //call the event loop
                    animate();
                };

                //*********************************
                //this is the name of the file you want to test. Make sure it's in the
                //the same directory as the script
                //load the specified sprite sheet
                img.src = 'zombieChase3.png';



            };

        </script>
    </head>

    //run init when body is loaded
    <body onload="init()">

        //just a canvas element
        <canvas id="canvas" width="500" height="500">
        You need a browser that supports the canvas element.
        </canvas>

    </body>
</html>

Thanks guys!

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$
//I suggest you use this polyfill for the rAF and cAF
//https://gist.github.com/paulirish/1579671


//Enclosing the module in a closure to avoid pollution of the global scope
(function (exports) {

  //Let's make each sprite an object, so that we have a reusable constructor
  function Sprite(config) {

    //A typical convention to not lose the context is to store it in another variable
    var self = this;

    //Setting a few configs for this object
    this.canvas = document.getElementById(config.canvas);
    this.context = this.canvas.getContext('2d');
    this.path = config.path;
    this.tiles = config.tiles;

    //We can add properties to the Sprite instance, like states and counters
    this.paused = false;
    this.tick = 0;

    //Let's load up the image when creating an instance
    var image = this.image = new Image();
    image.onload = function () {

      //When loaded, get the info
      self.width = image.width;
      self.height = image.height;

      //Start the animation via the instance's start method
      self.animate();

    }
    image.src = this.path;
  }

  //Let's extend the constructor to have methods

  //Unpauses the instance and runs animate
  Sprite.prototype.play = function () {
    this.paused = false;
    this.animate();
  }

  //Our animation loop function
  Sprite.prototype.animate = function () {

    //If say the state is paused, don't request the next frame
    //Otherwise, schedule the next frame
    if(this.paused) return;
    requestAnimationFrame(animate);

    //clear and draw
    this.canvas.width = this.canvas.width;
    this.context.drawImage(this.image, this.tick * this.width, 0, this.width, this.height, 0, 0, this.width, this.height);

    //reset it tiles reached
    if(++this.tick > this.tiles) this.tiles = 0;

  }

  //This one stops the animation
  Sprite.prototype.stop() {
    this.paused = true;
  }

  exports.load = function (config) {
    return new Sprite(config)
  }

}(this.Sprite = this.Sprite || {}));

//Extensibility. Don't limit yourself to a fixed set of configurations.
//Make your code reusable.
var sprite = Sprite.load({
  canvasId: 'canvas',
  path: 'zombieChase3.png',
  tiles: '5'
});

//you can control the sprite
sprite.play();
sprite.stop();

//Additionally, you can add something like play, pause, unload etc. which I just did
//Also, sprite sheets are usually big sheets containing more than one sequence per image
//you should consider that. If you Google "sprite sheets", you'll see.
\$\endgroup\$
1
  • \$\begingroup\$ +1 but only because the review is in the comments. I would be an even better post if you elaborated a little bit in plain text. \$\endgroup\$ Nov 21, 2013 at 3:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.