Wednesday, January 8, 2014

01 - Define Functions

Two Ways to Define Functions

There are two different ways to define functions in JavaScript. The first way is to name explicitly, as shown below:
 function helloWorld() {
    console.log("Hello world.");
}

helloWorld();

// Output:
// Hello world.
In the code above, a function renamed helloWorld is defined. When it's invoked, a line of "Hello world." is logged into the console.

We can also define an anonymous function. Since a function in JavaScript is also an object, the anonymous function can be assigned to a variable name. The function then can be invoked via the variable name. The following is an example:
var helloWorld = function () {
    console.log("Hello world.");
}

helloWorld();

// Output:
// Hello world.
There is a striking differences between the two ways to define functions. An explicitly named function can be invoked ahead of its definition, as shown in the example below:
helloWorld();

function helloWorld() {
    console.log("Hello world.");
}

// Output:
// Hello world.

The function helloWorld is defined when the code is loaded, so it can be invoked even though the function call appears ahead of its definition.

The same scenario does not work for anonymous functions. For example, an error will be thrown in the sample below:
helloWorld(); // TypeError: undefined is not a function

var helloWorld = function () {
    console.log("Hello world.");
}
The anonymous function gets defined when the code is loaded, but the variable helloWorld won't be defined (be assigned to the anonymous function) when the line of code get executed. Therefore, when we try to call the function via the variable name helloWorld, the value of the variable is still undefined.

Immediately Invoked Function Expression

A function can be invoked immediately when it's defined. The following is an example:
(function() {
    console.log("Hello world.");
})();

// Output:
// Hello world.
In the code above, an anonymous function is defined. There is a pair of parentheses after the function definition, which indicate that the function is invoked immediately. 

An immediately invoked function expression is a widely used design pattern in JavaScript. The singleton pattern can be implemented based on an immediately invoked function. The following is an exapmle:
var Singleton = (function() {
    function SingletonClass() {
    }
   
    var instance = new SingletonClass();
   
    return {
        getInstance: function() {
            return instance;
        }
    };
})();
var instance1 = Singleton.getInstance(); var instance2 = Singleton.getInstance(); console.log(instance1 === instance2);
// Output: // ture
Inside the outer anonymous function, a constructor SingletonClass is defined, and an instance of SingletonClass is created. In the returned instance of the anonymous function, an instance of SingletonClass is returned in the member function getInstance. Notice that the constructor SingletonClass is defined inside an anonymous function, no one else can invoke the constructor to create new instances.

Outside the anonymous function, the returned instance is assigned to a variable name Singleton, then the unique instance of SingletonClass can be accessible via the function Singleton.getInstance. If the function Singleton.getInstance is invoked for multiple times, only one instance of SingletonClass will be returned, so instance1 and instance2 are identical. It demonstrates that requirements of the singleton pattern have been fulfilled.

Immediately invoked functions can also be utilized to keep private variables inaccessible. More details will be discussed in other posts later. 

Thursday, January 2, 2014

JavaScript in Practice (2) - Fireworks Animation

Here we are going to animate fireworks, with JavaScript and HTML5. The animation result looks like the image blow:

If you are interested to the dynamic animation, please go the webpage http://jsfiddle.net/zhedahht/rKsyE/. Additionally, the source code is shared at https://github.com/zhedahht/JsInPractice/tree/master/Fireworks.

Now let's dive into the source code of the fireworks. Fireworks are defined as lots of moving particles. Particles are created with the following JavaScript constructor:

function Particle(pos, speed, resistance, gravity, size) {
    var curPos = {
        x: pos.x,
        y: pos.y
    };
    var curSpeed = speed;
  
    this.render = function(context, color) {
        context.fillStyle = color;
        context.beginPath();
        context.arc(curPos.x, curPos.y, size, 0, Math.PI * 2, true);
        context.closePath();
        context.fill();
    }
  
    this.update = function() {
        curSpeed.x = curSpeed.x * resistance + gravity.x;
        curSpeed.y = curSpeed.y * resistance + gravity.y;
      
        curPos.x += curSpeed.x;
        curPos.y += curSpeed.y;
    }
}

Each particle is response to render itself as a dot on a canvas, and to update its position. 

A group of particles are shot from time to time, which share some common properties such as color and the initial position before explosion. A group of particles in a single shot are defined as:

function ParticleGroup(pos, canvasSize, numberOfParticles) {
    var shotHeight = randomInRange(canvasSize.height * 0.50,
                                   canvasSize.height * 0.75);
    var life = 100;
    var age = 0;
    var particles = initParticles(pos, canvasSize);
    var color = pickColor();


    this.render = function(context) {
        var strColor = color.toString();
        
        particles.forEach(function(particle) {
            particle.render(context, strColor);
        });
    }
    
    this.update = function() {
        age++;


        updateColor();
        
        particles.forEach(function(particle) {
            particle.update();
        });
    }
    
    this.isDead = function() {
        return age >= life;
    }
    
    function initParticles(pos, canvasSize) {
        var particles = [];


        var particlePos = {
            x: pos.x,
            y: pos.y - shotHeight
        }


        var resistance = 0.985;
        var gravity = {
            x: 0,
            y: 0.005
        }
        var size = 2;
        
        var maxSpeed = randomInRange(2.4, 3.2);


        for(var i = 0; i < numberOfParticles; ++i) {
            var angle = randomInRange(0, Math.PI * 2);
            var linearSpeed = randomInRange(0, maxSpeed);
            var speed = {
                x: linearSpeed * Math.cos(angle),
                y: linearSpeed * Math.sin(angle),
            }
            
            var particle = new Particle(particlePos, speed, resistance,
                                        gravity, size);
            particles.push(particle);
        }
        
        return particles;
    }
    
    function updateColor() {
        var alpha = 1.0;
        var oldness = age / life;
        if (oldness > 0.90) {
            alpha = 10 * (1 - oldness);
            color.setAlpha(alpha);
        }
    }
}

The following constructor is to create fireworks, which is response to shot new group of particles from time to time, and remove groups when they get too old:

function Firework(pos, canvasSize, numberOfParticles) {
    var shots = [];
    this.render = function(context) {
        shots.forEach(function(shot) {
            shot.render(context);
        });
    }
    
    this.update = function() {
        removeDeadShots();
        
        shots.forEach(function(shot) {
            shot.update();
        });
    }
    
    this.shot = function() {
        var newShot = new ParticleGroup(pos, canvasSize, numberOfParticles);
        shots.push(newShot);
    }
    
    function removeDeadShots() {
        for(var i = 0; i < shots.length; ++i) {
            shot = shots[i];
            if (shot.isDead()) {
                shots.splice(i, 1);
            }
        }
    }
}

Usually there are many fireworks shooting at the same time. The following constructor create a group of fireworks:

 function FireworkGroup(canvasId, numberOfFireworks, numberOfParticles) {
    var fireworkGroupElement = document.getElementById(canvasId);
    var context = fireworkGroupElement.getContext("2d");
    
    var width = fireworkGroupElement.clientWidth;
    var height = fireworkGroupElement.clientHeight;
    
    var fireworks = initFireworkGroup(width, height);
    
    this.getFireworks = function() {
        return fireworks;<
    }

    this.render = function() {         context.fillStyle = "#010212";         context.fillRect(0, 0, width, height)                  fireworks.forEach(function(firework) {            firework.render(context);         });     }          this.update = function() {         fireworks.forEach(function(firework) {            firework.update();         });     }          this.shot = function() {         fireworks.forEach(function(firework) {            firework.shot();         });     }
    function initFireworkGroup(width, height) {         var fireworks = [];         for(var i = 0; i < numberOfFireworks; ++i) {             var pos = {                 x: Math.round((width / numberOfFireworks) * (i + 0.5)),                 y: height * 0.95             };             var canvasSize = {                 width: width,                 height: height             };
            fireworks[i] = new Firework(pos, canvasSize, numberOfParticles);             }                  return fireworks;     } }

With the code above, we can create fireworks on a canvas when the document is loaded:

$(document).ready(function () {
    makeFireworkGroup("canvasForfireworks", 3, 300);
});

function makeFireworkGroup(canvasId, numberOfFireworks, numberOfParticles) {     function shotFireworkGroup(fireworkGroup) {         var fireworks = fireworkGroup.getFireworks();         fireworks.forEach(function(firework) {             shotFirework(firework);         });     }          function shotFirework(firework) {         firework.shot();                  var wait = randomInRange(1200, 1600);         setTimeout(shotFirework, wait, firework);     }          function renderAndUpdate(fireworks) {         return function() {             fireworks.render();             fireworks.update();         };     }
    var fireworks = new FireworkGroup(canvasId,                                       numberOfFireworks,                                       numberOfParticles);     shotFireworkGroup(fireworks);          var renderAndUpdateFunc = renderAndUpdate(fireworks)     setInterval(renderAndUpdateFunc, 15); }

In the code above, 300 particles are shot in about every 1.5 seconds, and the canvas will be updated in every 15 milliseconds. 

If you find that there are more and more particles on your canvas, it means there are too many particles. You may decrease the number of particles in each shooting.

Finally, some utility functions are needed for randomness and particle colors, as listed below:

function randomInRange(min, max) {
    return Math.random() * (max - min) + min;
}

var pickColor = (function() {     var colors = [         new Color(0x00, 0xFF, 0xFF), // Aqua         new Color(0x8A, 0x2B, 0xE2), // BlueViolet         new Color(0xDC, 0x14, 0x3C), // Crimson         new Color(0xFF, 0x14, 0x93), // DeepPink         new Color(0x22, 0x8B, 0x22), // ForestGreen         new Color(0xAD, 0xFF, 0x2F), // GreenYello         new Color(0xFF, 0x69, 0xB4), // HotPink         new Color(0xCD, 0x5C, 0x5C), // IndianRed         new Color(0xF0, 0xE6, 0x8C), // Khaki         new Color(0x7C, 0xFC, 0x00), // LawGreen         new Color(0x00, 0xFA, 0x9A), // MediumSrpingGreen         new Color(0xFF, 0xA5, 0x00), // Orange         new Color(0x80, 0x00, 0x00), // Purple         new Color(0xFF, 0x00, 0x00), // Red         new Color(0x87, 0xCE, 0xEB), // SkyBlue         new Color(0xFF, 0x63, 0x47), // Tomato         new Color(0xEE, 0x82, 0xEE), // Violet         new Color(0xF5, 0xDE, 0xB3), // Wheat         new Color(0xFF, 0xFF, 0x00)  // Yellow               ];          return function() {         var index = Math.round(randomInRange(0, colors.length - 1));         return colors[index].clone();     } })();
function Color(red, green, blue, alpha) {     var r = red,         g = green,         b = blue,         a = alpha;              this.toString = function() {         if (a === undefined) {             return "rgb(" + r + "," + g + "," + b + ")";         }                  return "rgba(" + r + "," + g + "," + b + "," + a + ")";     }          this.setAlpha = function(newAlpha) {         a = newAlpha;     }          this.clone = function() {         return new Color(r, g, b, a);     } }

Wednesday, December 25, 2013

JavaScript in Practice (1) - Cartoon Snow Animation

Christmas is a season about snow and Santa Claus. Let's do the same thing with JavaScript: Animate snow on the background image about Santa Claus is delivering presents to children, as shown in the following picture.
If you are interested in what the animation looks like, please go the link http://jsfiddle.net/zhedahht/2uD5x/1/. The source code is shared at https://github.com/zhedahht/JsInPractice/tree/master/MerryChristmas. The following is the explanation of the code.

First, let's have a look about the definition of snow, as listed below:

function Snow(snowSettings) {
    this.snowSettings = snowSettings;

    this.radius = randomInRange(snowSettings.radiusRange);
    this.initialX = Math.random() * snowSettings.maxX;
    this.y = Math.random() * snowSettings.maxY;
    this.speedY = randomInRange(snowSettings.speedYRange);
    this.speedX = snowSettings.speedX;
    this.alpha = randomInRange(snowSettings.alphaRange);
    this.angle = Math.random(Math.PI * 2);
    this.x = this.initialX + Math.sin(this.angle);
    this.moveX = randomInRange(snowSettings.moveXRange);
}

Snow.prototype.render = function(canvasContext) {
    canvasContext.fillStyle = "rgba(255, 255, 255, " + this.alpha + ")";
    canvasContext.beginPath();
    canvasContext.arc(this.x, this.y, this.radius, 0 ,Math.PI * 2, true);
    canvasContext.closePath();
    canvasContext.fill();
}

Snow.prototype.update = function() {
    this.y += this.speedY;
    if (this.y > this.snowSettings.maxY) {
        this.y -= this.snowSettings.maxY;
    }

    this.angle += this.speedX;
    if (this.angle > Math.PI * 2) {
        this.angle -= Math.PI * 2;
    }

    this.x = this.initialX + this.moveX * Math.sin(this.angle);
}

function randomInRange(range) {
    var random = Math.random() * (range.max - range.min) + range.min;
    return random;
}

We can see that a piece of snow is rendered as a circle, in somewhat transparent white color. Each piece of snow moves along a sinusoidal curve. Our model is quite simple, but the result looks pretty good for cartoon snow animation.

The size, position, and speed are random values in ranges defined in a setting class:

function SnowSettings(radiusRange, maxX, maxY, speedYRange,
                        speedX, alphaRange, moveXRange) {
    this.radiusRange = radiusRange;
    this.maxX = maxX;
    this.maxY = maxY;
    this.speedYRange = speedYRange;
    this.speedX = speedX;
    this.alphaRange = alphaRange;
    this.moveXRange = moveXRange;
}

We use the following values to config snow settings while initializing:

function initSnow(width, height) {
    var radiusRange = new Range(3, 10),
        speedYRange = new Range(1, 3),
        speedX = 0.05,
        alphaRange = new Range(0.5, 1.0),
        moveXRange = new Range(4, 18);
        
    var snowSettings = new SnowSettings(radiusRange,
                    width, 
                    height, 
                    speedYRange, 
                    speedX, 
                    alphaRange, 
                    moveXRange);
    
    var snow = [];
    var snowNumber = 200;
    for(var i = 0; i < snowNumber; ++i) {
        snow[i] = new Snow(snowSettings);
    }
    
    return snow;
}

function Range(min, max) {
    this.min = min;
    this.max = max;
}

Besides pieces of snow, there is also a background image, therefore we also need a class containing both snow and background image. The container class is defined below:

function ChristmasSnow(canvasId, imagePath) {
    var snowElement = document.getElementById(canvasId);
    this.canvasContext = snowElement.getContext("2d");
    
    this.width = snowElement.clientWidth;
    this.heigth = snowElement.clientHeight;
    
    this.image = initImage(imagePath);
    this.snow = initSnow(this.width, this.heigth);
}

function initImage(imagePath) {
    var image = new Image();
    image.src = imagePath;
    return image;
}

ChristmasSnow.prototype.render = function() {
    this.canvasContext.drawImage(this.image, 0, 0);
    
    for(var i = 0; i < this.snow.length; ++i) {
        this.snow[i].render(this.canvasContext);
    }
}

ChristmasSnow.prototype.update = function() {
    for(var i = 0; i < this.snow.length; ++i) {
        this.snow[i].update();
    }
}
    
With the two class above, we could define an API to create snow with a background image into a HTML5 canvas, as listed below:

SnowLib = {
    makeSnow: makeSnow
};

function makeSnow(canvasId, imagePath) {
    var christmasSnow = new ChristmasSnow(canvasId, imagePath);
    var renderAndUpdateFunc = renderAndUpdate(christmasSnow)
    setInterval(renderAndUpdateFunc, 15);
}

function renderAndUpdate(christmasSnow) {
    return function() {
        christmasSnow.render();
        christmasSnow.update();
    }
}

Users can create their own snow animation with the API SnowLib.makeSnow.