Skip to content Skip to sidebar Skip to footer

How Do Js Animations Work?

Im trying to understand how to make a javascript animation run smoothly and I've been reading some answers around here and I found something I don't understand. Here is the link to

Solution 1:

Overview

Generally with animations, you have three components

  1. Update
  2. Draw
  3. Timer

These are run within a loop, called the animation loop. A typical animation loop might look like the following (I will explain all of the functions in detail below):

function animate() {
    update(); // Executes all game logic and updates your world

    draw(); // Draws all of the animated elements onto your drawing context

    timer(); // Controls the timing of when animate will be called again
};

animate(); // Start animating

The animation loop is the main flow controller of what goes on inside your animation. Basically the code inside the animation loop is called over and over again. Each execution of the animate function constitutes a frame. During a frame, your world is updated and redrawn on the screen. The frequency with which the animate function runs is called the frame rate, and is controlled by the timer.

You will also need a reference to the drawing context, which will be used to hold the elements you wish to animate, also known as sprites:

// Create a variable with a reference to our drawing context
// Note this is not the same as using a canvas element
var canvas = document.getElementById("canvas");

Update

Update is responsible for updating the status of each item you wish to animate, once per frame. To take a simplistic example, say you have an array containing three cars, each with an x and y position and a velocity. On each frame, you want to update the position of the car to reflect the distance it should travel based on its velocity.

Our cars array, and the code used to generate a car might look like this:

// A method to create new cars
var Car = new Car(x, y, vx, vy) {
    this.className = "car"; // The CSS class name we want to give the car
    this.x = x || 0; // The x position of the car
    this.y = y || 0; // The y position of the car
    this.vx = vx || 0; // the x component of the car's velocity
    this.vy = vy || 0 // the y component of the car's velocity
};

// A function that can be called to update the position of the car on each frame
Car.prototype.drive = function () {
    this.x += this.vx;
    this.y += this.vy;

    // Return an html string that represents our car sprite, with correct x and y 
    // positions
    return "<div class='" 
            + this.className 
            + "' style='left:" 
            + this.x 
            + "px; top:"
            + this.y
            + "px;'></div>";
};

// Create a variable to hold our cars
var cars = [
    new Car(10, 10, 5, 3),
    new Car(50, 22, 1, 0),
    new Car(9, 33, 20, 10)
];

When we call update, we will want to call the drive method of each car in order to move the car sprites around the screen. This drive method will return an html string that represents the sprite, including it's current position. We will want to append this string to a variable that can be used to set the inner HTML of the canvas div:

// An empty string that will be used to contain the innerHTML for our canvas
var htmlStr = "";

// Update the position of each car
function update() {
    // Clear the canvas content
    htmlStr = "";

    for (var i = 0, len = cars.length; i < len; i++) {
        // Add the car sprite to the html string to be rendered
        htmlStr += cars[i].drive();
    }
};

Draw

When the update function is done outputting our sprites, we will need to actually draw the elements on the canvas. In order to do this, we use the innerHTML method of the canvas variable, passing it in htmlStr, which contains markup used to represent all of the sprites. This will ask the browser to parse the text and spit DOM elements out on the screen:

function draw() {
    // Parse the text containing our sprites and render them on the DOM tree
    canvas.innerHTML = htmlStr;
};

You could argue that there are better ways to do this, and there probably are. However, in the interest of keeping it simple, I kept it simple. This is also not the most performant method, as the DOM API is very slow. If you look at the system resource usage of this application, most of it will be hogged by innerHTML calls. If you want a faster context for drawing, you should use HTML 5 canvas.

Timer

Finally, you need some way of calling animate over and over again. You could do this a couple of ways. First, there is good old setTimeout:

setTimeout(animate, 0);

This instructs the browsers to call animate after a delay of 0ms. In practice, animate will never execute after a delay of 0ms. The minimum resolution of setTimeout in most browsers is around 15ms, but even that is not guaranteed, due to the way the UI thread works in javascript.

A better solution is to use requestAnimationFrame, which basically tells the browser you are doing animation. The browser will do a bunch of nice optimizations for you. However, this is not fully supported across browsers. You should use this solution for a cross-browser requestAnimationFrame polyfill:

http://paulirish.com/2011/requestanimationframe-for-smart-animating/

Then you can use:

requestAnimFrame(animate);

In the end your finished program will look something like:

// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       || 
          window.webkitRequestAnimationFrame || 
          window.mozRequestAnimationFrame    || 
          window.oRequestAnimationFrame      || 
          window.msRequestAnimationFrame     || 
          function(/* function */ callback, /* DOMElement */ element){
            window.setTimeout(callback, 1000 / 60);
          };
})();

// Create a variable with a reference to our drawing context
// Note this is not the same as using a canvas element
var canvas = document.getElementById("canvas");

// A method to create new cars
var Car = new Car(x, y, vx, vy) {
    this.className = "car"; // The CSS class name we want to give the car
    this.x = x || 0; // The x position of the car
    this.y = y || 0; // The y position of the car
    this.vx = vx || 0; // the x component of the car's velocity
    this.vy = vy || 0 // the y component of the car's velocity
};

// A function that can be called to update the position of the car on each frame
Car.prototype.drive = function () {
    this.x += this.vx;
    this.y += this.vy;

    // Return an html string that represents our car sprite, with correct x and y positions
    return "<div class='" 
            + this.className 
            + "' style='left:" 
            + this.x 
            + "px; top:"
            + this.y
            + "px;'></div>";
};

// Create a variable to hold our cars
var cars = [
    new Car(10, 10, 5, 3),
    new Car(50, 22, 1, 0),
    new Car(9, 33, 20, 10)
];

// An empty string that will be used to contain the innerHTML for our canvas
var htmlStr = "";

// Update the position of each car
function update() {
    // Clear the canvas content
    htmlStr = "";

    for (var i = 0, len = cars.length; i < len; i++) {
        // Add the car sprite to the html string to be rendered
        htmlStr += cars[i].drive();
    }
};

function draw() {
    // Parse the text containing our sprites and render them on the DOM tree
    canvas.innerHTML = htmlStr;
};

function animate() {
    update(); // Executes all game logic and updates your world

    draw(); // Draws all of the animated elements onto your drawing context

    requestAnimFrame(animate); // Controls the timing of when animate will be called again
};

animate(); // Start animating

Conclusion

There is a lot of room for optimization, tweaking, abstraction, and a whole lot of other goodness that I didn't go into here. There are a ton of ways to implement update and draw, and all have there pros and cons.

However, every animation you will ever write will use some sort of animation loop and they all have this basic architecture. Hopefully, this illustrates the basics of an animation in javascript.


Solution 2:

The basic idea is the following:

  • You want to move a DIV from 0,0 to 100,0 in 1 second.
  • You'd like it to have 50 frames per second (using a setTimeout(fun, 20))
  • However, since the callback is not guaranteed to run in exactly 20ms, your callback needs to figure out when it was actually run. For example, let's say your animation started at time X but your first animation callback wasn't called until X+5ms. You need to calculate the position that the div should be at 25ms of the animation, your animation callback can't assume 50 even steps.

Here's a very simple code sample. I don't usually code with globals but this is the easiest way to show the technique.

http://jsfiddle.net/MWWm6/2/

var duration = 1000; // in ms
var startTime; // in ms
var startX = 0;
var endX = 500;
// 0 means try to get as many frames as possible
// > 1: Remember that this is not guaranteed to run as often as requested
var refreshInterval = 0;
var div = document.getElementById('anim');

function updatePosition() {
    var now = (new Date()).getTime();
    var msSinceStart = now - startTime;
    var percentageOfProgress = msSinceStart / duration;
    var newX = (endX - startX) * percentageOfProgress;
    div.style.left = Math.min(newX, endX) + "px";
    if (window.console) {
        console.log('Animation Frame - percentageOfProgress: ' + percentageOfProgress + ' newX = ' + newX);
    }
    if (newX < endX) {
        scheduleRepaint();
    }
}

function scheduleRepaint() {
    setTimeout(updatePosition, refreshInterval);
}

div.onclick = function() {
    startTime = (new Date()).getTime();
    scheduleRepaint();
}

Solution 3:

This post describes the best way to do animations: http://paulirish.com/2011/requestanimationframe-for-smart-animating/

That will run at 60fps, or as fast as possible.

Once you know that, then you can decide how long you want your animation to take and how far the object is moving, then work out how far it has to move each frame.

Of course, for smooth animations, you should use CSS Transitions where ever possible – take a look at http://css3.bradshawenterprises.com.


Solution 4:

The quick answer is that setTimeout doesn't guarantee that the callback will be executed n number of milliseconds after you call it. It just guarantees that it will be executed no sooner than n milliseconds from that time. John Resig covers some of this in this piece.

For this reason, you need to check what time your animation callback has actually executed, not what time you've scheduled it with setTimeout.


Solution 5:

I've written a blogpost which demonstrates the concept of "time based" animation using canvas and a sprite sheet.

The example animates a sprite and takes into account the elapsed time to make sure it remains consistent in different framerates. You could easily apply this into moving an element around a page by changing the code slightly. The concepts related to timing and animation itself remain pretty much the same.

http://codeutopia.net/blog/2009/08/21/using-canvas-to-do-bitmap-sprite-animation-in-javascript/

(I always feel so spammy leaving answers that basically just link to my blog, even if the link is actually entirely relevant :D )


Post a Comment for "How Do Js Animations Work?"