Table of Contents
- Preparation
 - 
    Example projects
    
- 
        Project 1 – Basic app with animated transitions
        
- Mission Checklist
 - Project Preparation
 - 1. Setting up the project and GulpJS
 - 2. Setting up the canvas and CreateJS library
 - 3. Defining scene as container inheritance
 - 4. Adding static stylish menu
 - 5. Displaying another scene after menu selection
 - 6. Animating transition between scenes
 - 7. Optimizing for retina display
 - Further challenges
 
 - Project 1B – DOM-based app with animated transitions
 - Project 2 – Rain or Not?
 - Project 3 – Solar System
 - 
        Project 4 – Countries Area
        
- Why is this project awesome?
 - Preparing the project
 - Step 1 – Building the app layout with flex
 - Step 2 – Listing the countries data
 - Step 3 – Basic list selection and calculation
 - Step 4 – Styling the radio and checkbox list
 - Step 5 – Drawing the chart
 - Step 6 – Adding the info panel and global app style
 - Summary
 
 - Project 4B – Drawing charts on canvas
 - Change Log
 
 - 
        Project 1 – Basic app with animated transitions
        
 
About author
Makzan focuses on web development and game design. He has over 14 years of experience in building digital products. He has worked on real-time multiplayer interaction games, iOS applications, and rich interactive websites.
He has written 3 books and 1 video course on building a Flash virtual world and creating games with HTML5 and the latest web standards. He is currently teaching courses in Hong Kong and Macao SAR. He writes tutorials and shares his thoughts on makzan.net.
Books by Makzan
Makzan has published 3 books and 1 video course.
- HTML5 game development hotshot – Step by step game projects
 - HTML5 game development video
 - HTML5 games development for beginners: A beginner’s guide
 - Flash multiplayer virtual world
 
Thanks
Writing book is never easy. I would like to thank my family to support my writings.
Also, great thanks to the following friends who keep giving feedback a during the book writing process.
- Franklin Ng
 
Preface
This book focuses on building canvas-based application and games with the CreateJS suite. The gap between web application and native application is not as big as usual, thanks to the performance boost of the browser engine and the device processing power. By using web technologies in app development, we can benefit from the flexibility of web programming. Canvas and CreateJS allows us to go beyond static display. By combining the DOM elements and rich media rendering in canvas, we can provide immersive experience to our users.
Do keep in mind that we are never locked to any technology. Canvas based animation can works well together with the CSS animation and transition. We can also go for the hybrid approach by making use of the native navigation together with the high performance web view to render our content.
Hope you enjoy the book examples and go further by creating your rich interactive applications.
Code Example
You can find the code example in the following GitHub repository.
http://mak.la/cjs-examples
You can git clone the project to your machine so that you can update the code by using git commands. Alternatively, you may click “Download Zip” to download the whole bundle.

Download source code
Preparation
In this part, we setup our environment and get ready for getting our hand dirty to code the example project.
Tools we need in this book
We need the following tools to go through our projects.
Operating system
Mac OS X is preferred to build iOS app. Otherwise, Linux and Windows works well as development machine.
Required software
- Code editor 
    
Any text editor works. I recommend Sublime Text if you don’t have a preference.
 - Modern web browsers
    
Browsers are needed to test and debug our web app rendering.
 - Android and iOS Simulator 
    
We need simulator to test how our web apps run in mobile operating system. We’ll use Genymotion or official simulator for Android. We need Mac OS X and Xcode for the iOS simulator.
 
Optional
- Adobe Flash
    
We will create some vector animations in Adobe Flash. CreateJS library integrates with the flash IDE so that we can export the timeline animation directly into the canvas. This is optional because the course code contains the exported animation we need. But if you want to change the animation, you may need Flash IDE to modify the source
.flafile. 
       
 | 
    
      What version of Adobe Flash supported?The exporting tool exists as a plugin in Adobe CS6. In CC 17.0, the tool appears as a command. In CC 17.1. The CreateJS toolkit is deeply integrated into the application.
There are a new kind of   | 
  
- PhoneGap build service 
    
We need to pack the web pages and asset files into binary application in order to deploy it on app stores. PhoneGap is one of the solutions. The setup, however, may not be easy enough for web designer. PhoneGap Build provides a web service that lets us upload the client-side code and files. Then it builds the binary applications for different platforms without us handling the compilation environment.
 
CoffeeScript compiling tools
When we write the logic in CoffeeScript, we need to compile them into JavaScript. You can choose your own method to compile the .coffee file into .js. Some compilers suggestion lists here in case you don’t have a favorite one yet.
- GruntJS GruntJS is a command line task runner for JavaScript. It allows developer to define how some inputs can be manigulated and transformed into the destination files.
 
- GulpJS GulpJS provides Grunt-simira task runner. The different is that GulpJS uses the concept of pipelane. That means we can write the tasks in cleaner way by ‘chaining’ the tasks.
 
- LiveReload LiveReload is a GUI tool that provides several handy features. It auto refreshes the browser when you save you files. It also compiles the preprocessor files for you, including CoffeeScript, SCSS and more.
 - CodeKit CodeKit is another GUI tool with similar features from LiveReload. It is mac only and provides shortcut to install commonly-used libraries into your projects.
 
Pre-Request
This book assumes you have basic knowledge of client-side web development techniques. In order to follow the examples, you need to know basic HTML and CSS. You’ll learn some intermediate JavaScript programming skills too, but knowing some fundamental JS would be great.
The following is a list of online resource that you may learn yourself alongside the book examples.
HTML and CSS
Here is a list of resources if you need to get started learning HTML and CSS, the browser technology that display information and formatting styles.
- 
HTML and CSS book by Jon Duckett.
    
This book helps you learn the most basic web design skills from scratch. The book demonstrates web design technologies with useful graphs and code examples.
 - 
Learn to Code HTML & CSS by Shay Howe. 
    
Shay Howe shows the essential techniques to craft your own web page. It’s easy to follow and you’ll learn the fundamentals to get started as web designer.
 - 
Learn CSS Layout by Bocoup.
    
There are many ways to lay out content in CSS. This website provides comprehensive reference on all major layout approaches.
 - 
Learn to Code Advanced HTML & CSS by Shay Howe. 
    
After you get familiar with basic HTML and CSS, you may read this resource that will take you to the next level of web design.
 
JavaScript
We use a lot of JavaScript in this book. All the interaction and canvas graphics are done via script. Here are several resources that worth reading.
- 
JavaScript and jQuery 101 by jQuery.
    
The jQuery learning center contains detail guides to learn the concept of JavaScript and jQuery. It’s a good place to reference different object types. It also teaches you the jQuery concept and essential code.
 - 
Eloquent JavaScript by Marijn Haverbeke. 
    
This is a must have book if you want to learn JavaScript inside out. It covers the JavaScript programming for both client-side and browser-side.
 - 
JavaScript book by Jon Duckett.
    
Jon Duckett has the ability to present programming skills in an easy-to-follow way. This JavaScript book demonstrates how JavaScript and jQuery works in beautiful and useful illustrations and charts.
 - 
JavaScript Best Practices by Thinkful.
    
JavaScript can be evil if writing it badly. JavaScript Best Practices show different tricks to write maintainable and less buggy code.
 - 
JavaScript: The Right Way by William Oliveira.
    
This is a big list of JavaScript resources containing latest practices and modern libraries.
 - 
JavaScript: The Good Part by Douglas Crockford.
    
This book shows the beauty part of JavaScript. It presents the readers how JavaScript is different than the traditional object-oriented programming languages. JavaScript has its own way to write OOP.
 
CoffeeScript
CoffeeScript is beauty way to write JavaScript. It provides syntax that makes writing JavaScript application much easier. We will use CoffeeScript in some examples. Please note that CoffeeScript is JavaScript. It is like shorthand of JavaScript. When we write our program, we still think in JavaScript way. So you still need to learn JavaScript well.
- 
Official site of CoffeeScript
    
The official site contains all the syntax that we need to get started.
 - 
JS2Coffee converter
    
At the beginning of learning CoffeeScript, you may find yourself struggling between JS and Coffee. This converter is a handy tool to convert the given script between JavaScript and CoffeeScript.
 - 
The little book of CoffeeScript
    
If you prefer reading book than website, you’ll find this book useful as the reference guide to the CoffeeScript syntax.
 - 
CoffeeScript Cookbook
    
This cookbook contains lots of practical examples for your daily JS development needs.
 - 
CoffeeScript Ristretto
    
This book provides a journey of JavaScript and CoffeeScript development process. The examples in this book demonstrates how we can write code to get the job done at the first step, then fine tuning the code to make it more readable and maintainable.
 
Why CreateJS?
There are so many HTML5 game libraries out there. Why I choose CreateJS?
It is not a game engine. It is a library that takes graphics and animation drawing its main priority. Just like every one used Flash to make game, but still other creatives used Flash to build interactive applications. This is the same while using the CreateJS.
As a bonus, CreateJS integrates deeply in Adobe Flash. This allows us to create and export timeline-based animation to CreateJS’ canvas drawing JavaScript directly. That’s a very handy workflow if you use it probably.
Quick demo – advertisement animation
Here is a quick demo to show how Adobe Flash exports an advertisement animation and plays in HTML5 with CreateJS. The following example is the classic tween animation directly exported from Flash. I didn’t edit the output code.

Classic tween transition
How we make use of the generated code in our CreateJS project? We can instaniate the exported object add add it to the display list. The object name follows the name we set in the Flash IDE, either the linked class name or the library name. Then we can just add it to the CreateJS hierarchy by as using a normal Sprite object.
After we include the exported JavaScript file into our project, we can add the movieclip from the .fla library to the canvas display list by using the following code snippet. Here we assumed the clip name in Flash library is called ExampleSprite.
1 var sprite = new ExampleSprite();
2 this.stage.addChild(sprite);

Canvas output of Flash ad
I have created a video demonstrating the process to make this advertisement and export the animation to CreateJS. You can find the video in the following link.
Example projects
I like to learn via practical projects. That’s why I make this book full of example projects that you can follow and modify them to fit into your real world projects.
We will build the following projects.
- 
Project 1: Basic app with animated transitions
    
In this project, we build a very basic canvas based app. The app demonstrates how we can create animated transition.
 - 
Project 1B: DOM-based app with animated transitions
    
In this project, we modify the project 1 to make all the content an HTML DOM element. This allows the content to be accessible and yet we keep the animation in Canvas.
 - 
Project 2: Rain or Not?
    
In this project, we separate the code into MVC model. That is dividing the code into Modal (Data), View and Controller modules. We also listen to the gyroscope in the mobile device to create a subtle motion parallax offset effect.
 - Project 3: Our solar system
 - Project 4: Drawing charts
 
Project 1 – Basic app with animated transitions
In this project, we are going to build a designer portfolio. There is stylish navigation menu, animated transitions and static photos viewer.

Screenshot of the app example
I have recorded a video of the app example that shows the animated transition. You may take a look to have a better understandings on what we are building.
Mission Checklist
We are going to build the portfolio project by following these tasks.
- Setting up the project and GulpJS.
 - Setting up the canvas and CreateJS library.
 - Defining scene as container inheritance.
 - Adding static stylish menu.
 - Displaying another scene after menu selection.
 - Animating transition between scenes.
 - Optimizing the rendering for retina display.
 
Project Preparation
Throughout the project, we need several graphic files to complete the project. Please download the files via the following link.
http://mak.la/cjs-proj1.zip
After you downloaded the file, you will see the following files in the bundle.

PNG files for project 1
images/
images/header.png
...
scripts/transitions.js
graphics_src/transitions.fla    
The transitions.js file is the animated transition exported from the Adobe Flash. If you don’t have the Adobe Flash, you can just use this file who comes with 2 transitions to follow the example. If you have Flash, however, you can open the transitions.fla file and customize the transition animation. You may even create new transition effects. We will talk about this later.
1. Setting up the project and GulpJS
In this step, we prepare the compiling environment for our project.
Preparation
Make sure we have nodejs and npm installed. npm is the package modules manager for nodejs. It’s included inside nodejs instaler package.
Time for Action
In this steps, we will initial our project with the GulpJS compiling automation setup.
- First, let’s create a folder for all the files in this project. The following is the initial file structure setup.
    
Gulpfile.jsGulpfile.coffeepackage.jsonapp/index.htmlapp/images/app/scripts/app/scripts/app.coffeeapp/scripts/setting.coffeeapp/styles/app/styles/app.css - We can generate the 
package.jsonfile via thenpm initmethod. After we go through thenpm initprocess, we can then install the plugins via the following shell command.$npminstallgulpcoffee-scriptgulp-coffeegulp-concatgulp-order--save-dev - After step 2, node.js should have written the following content into the 
package.jsonfile. Check if you get similar result.package.json
1{2"name":"JackPortfolio",3"version":"1.0.0",4"description":"An example for my book Rich interactive app development with \5CreateJS",6"main":"Gulpfile.js",7"scripts":{8"test":"echo \"Error: no test specified\" && exit 1"9},10"author":"Makzan",11"license":"MIT",12"devDependencies":{13"coffee-script":"^1.8.0",14"gulp":"^3.8.9",15"gulp-coffee":"^2.2.0",16"gulp-concat":"^2.4.1",17"gulp-order":"^1.1.1"18}19}
 - Gulp always looks for the 
Gulpfile.jsfile when we execute the gulp tasks. If we want to write the Gulp tasks in CoffeeScript syntax, we need to load the CoffeeScript compiler in theGulpfile.jsand include the.coffeeversion of the GulpFile.gulp.js
1require('coffee-script/register');2require('./Gulpfile.coffee');
 - Now we can write our Gulp pipeline in CoffeeScript. The following configuration defines how the compiler should compile our source files. It also defined a 
watchtask that watch any changes of.coffeefile and go through thejstask automatically.gulp.coffee
1gulp=require'gulp'2coffee=require'gulp-coffee'3concat=require'gulp-concat'4order=require'gulp-order'56gulp.task'js',->7gulp.src[8'./app/scripts/setting.coffee'9'./app/scripts/app.coffee'10]11.pipecoffee()12.pipeconcat'app.js'13.pipegulp.dest'./app/scripts/'141516gulp.task'watch',->17gulp.watch'./app/scripts/**/*.coffee',['js']1819gulp.task'default',['js','watch']
 - We create 2 CoffeeScript files to see if our 
Gulpfileworks. They areapp.coffeeandsetting.coffee. We will add real code logic into these files in next step.app.coffee
1console.log"App – Testing GulpJS setup."
setting.coffee
1console.log"Setting – Testing GulpJS setup"
 - Now we can run 
gulpin the terminal and we should see anapp.jsfile is generated with the following content.The generated app.js
1(function(){2console.log("Setting – Testing GulpJS setup");34}).call(this);56(function(){7console.log("App – Testing GulpJS setup.");89}).call(this);
 
If you get the same result, that means our setup works.
       
 | 
    
       You may need to run the local installed   | 
  
What’s happening?
We just set up the assets compiling tool chain. After setting up the building streamline, we are ready to dig into the early project development in next task.
Further enhancement
In this example, we used GulpJS to compile our sources. If you prefer GUI setup, you may take a look at the LiveReload and CodeKit project.
2. Setting up the canvas and CreateJS library
In this step, we setup the canvas and the CreateJS for the project.
Preparation
We need the CreateJS library. The easiest way to include the CreateJS is via the distribution of content delivery network.
http://code.createjs.com/
Optionally, we can download the code from the CreateJS github repostory and host the files ourselves.
https://github.com/createjs
Time for Action
Let’s follow the following steps to setup our canvas and CreateJS library.
- In the 
index.html, we prepare the basic HTML structure.index.html
1<!DOCTYPEhtml>2<htmllang="en">3<head>4<metacharset="utf-8">5<metaname="viewport"content="width=device-width, initial-scale=1">6<metaname="apple-mobile-web-app-capable"content="yes">7<title>JackPortfolio</title>8<linkrel="stylesheet"href="styles/app.css">9</head>10<body>11<!--Theappelement-->12<divid="app">13<canvasid="app-canvas"width="300"height="400"></canvas>14</div>1516<!--WeloadtheJavaScriptaftercontent-->17<scriptsrc="http://code.createjs.com/easeljs-0.7.1.min.js"></script>18<scriptsrc="http://code.createjs.com/tweenjs-0.5.1.min.js"></script>19<scriptsrc="http://code.createjs.com/movieclip-0.7.1.min.js"></script>20<scriptsrc="scripts/app.js"></script>21</body>22</html>
 - We have minimal styling in this task because our focus is on the canvas element. Add the following CSS to the 
styles/app.cssfile.styles/app.css
1/* Ensure the box sizing is the modern one. */2*{3box-sizing:border-box;4}56/* Basic reset */7body{8margin:0;9padding:0;10}111213/* canvases sit inside the #app frame. It’s similar to layers. */14#app>canvas{15position:absolute;16top:50%;17left:50%;18height:400px;19width:300px;20margin-top:-200px;21margin-left:-150px;22}
 - We created a file named 
setting.coffeewhich holds our global app configuration variables. Add the following width and height setting to the file.setting.coffee
1#aglobalappobject.2this.exampleApp?={}34#Configurations5this.exampleApp.setting={6width:3007height:4008}
 - Then we create the entry point of our app in the 
app.coffee. Add the following code to the file.app.coffee
1#aglobalappobject.2this.exampleApp?={}34#alias5cjs=createjs6setting=this.exampleApp.setting789classApp10#Entrypoint.11constructor:->12console.log"Welcome to my portfolio."13@canvas=document.getElementById("app-canvas")14@stage=newcjs.Stage(@canvas)1516cjs.Ticker.setFPS6017cjs.Ticker.addEventListener"tick",@stage#makesurethestagerefreshdr\18awingforeveryframe.192021#Starttheapp22newApp()
 - We have created the app’s foundation. Although we don’t see any content yet, the app structure is ready and we can add our scene to the app in next step.
 
What’s happening?
We just created the basic canvas app and CreateJS setup. In next step, we’ll build our scene. Let’s take a look at each part of code in this step.
Viewport
<meta name="viewport" content="width=device-width, initial-scale=1">
We target the app to be a mobile application. So we need to set a viewport. Mobile web browser simulate the device width as a desktop monitor to provide a better viewing experience for most desktop-only website. Viewport lets web designer tells the mobile browser the display configurations we want.
The default viewport of mobile web browser is about 980px. If we have created the styles dedicated to narrow screen, such as 320px width, we should change the viewport width to reflect the real device width.
       
 | 
    
       If you want to provide an app like experience where users cannot zoom the view, you may consider adding the   | 
  
Web app capable
<meta name="apple-mobile-web-app-capable" content="yes">
We want to provide an app experience to the user. When user add the web app into homescreen, normal web pages act as bookmark. Tapping on them launch the mobile safari. After we set the apple-mobile-web-app-capable, the home screen bookmark acts like a real app. It has its own we view without the Safari user interface. It also has its own app switching screen in the multitask screen when user clicked the home button twice.
Default value when variable is undefined
this.exampleApp ?= {}
The equivalent way in JavaScript is:
if (this.exampleApp == null) this.exampleApp = {}
We can also express the same meaning with the following line, which looks cleaner.
this.exampleApp = this.exampleApp || {}
Centering the canvas
The canvas has fixed dimention. We can use the following styles to center aligning the canvas at the middle of the page.
1 #app > canvas {
2   position: absolute;
3   top: 50%;
4   left: 50%;
5   height: 400px;
6   width: 300px;
7   margin-top: -200px;
8   margin-left: -150px;
9 }
The code is inspired from the following CSS-Tricks website which shares different styling approaches to center elements.
http://css-tricks.com/centering-css-complete-guide/
CreateJS class
The class in CreateJS allows us to define a class definition and then we can create instance via the new method.
1 class App
2   # Entry point.
3   constructor: ->
4   
5 # Start the app
6 new App()
The simplicity of the CoffeeScript is it allows us to define and extends new classes.
Let’s take a look at the JavaScript our code generates.
 1 var App;
 2 
 3 App = (function() {
 4   function App() {}
 5 
 6   return App;
 7 
 8 })();
 9 
10 new App();
In JavaScript, we can create a new object by new a defined function.
3. Defining scene as container inheritance
In this step, we define the Scene class which every page view builds on top of it.
Time for Action
Let’s follow the steps to define a Scene class.
- We create a dedicated file for the scenes definition. Add the following code to the 
scenes.coffee.scenes.coffee
1#aglobalappobject.2this.exampleApp?={}34#alias5cjs=createjs6setting=this.exampleApp.setting78classSceneextendscjs.Container9constructor:(bgColor='blue')->10#CreateJSsuperconstructor11@initialize()1213#Drawashapeasthebackgroundcolor14ifbgColor!=undefined15shape=newcjs.Shape()16shape.graphics17.beginFillbgColor18.drawRect0,0,setting.width,setting.height1920#Addtheshapetothedisplaylist,viausingaddChild21@addChildshape2223#exporttoglobalappscope24this.exampleApp.Scene=Scene
 - In the 
app.coffee, we create the Scene instance and add it to the stage. This is a testing scene, we are going to change it to the real scene in next step.app.coffee
1Scene=this.exampleApp.Scene23classApp4constructor:->5...6#Temporarytestingscene7testScene=newScene('gold')8@stage.addChildtestScene
 - We have created a new file 
scenes.coffee, we need to include it into the GulpJS pipeline. Add the file into thegulp.srcarray.Gulpfile.coffee
1gulp.task'js',->2gulp.src[3'./app/scripts/setting.coffee'4'./app/scripts/scenes.coffee'5'./app/scripts/app.coffee'6]7.pipecoffee()8.pipeconcat'app.js'9.pipegulp.dest'./app/scripts/'
 
What’s happening?
We have defined a Scene class and added a testing scene object to the stage.
Vector shape drawing
A shape is vector graphic that we express in mathematics. It’s like giving instruction on what the shape should look like.
1 shape = new cjs.Shape()
2 shape.graphics
3   .beginFill "white"
4   .drawRect 0, 0, 100, 50  
For every created display object, we need to add it to the display list. The following code assumes that we are adding the shape to a container.
@addChild shape    
If we are adding the shape to the stage, we can call the stage.addChild because the stage is a container.
Class inheritance in CoffeeScript
The CoffeeScript inheritance took us 3 lines to inherit class.
1 class Scene extends cjs.Container
2   constructor: (bgColor='blue')->        
3     @initialize()
In the generated code. It would take 13 lines of code in JavaScript, not including the _extends helper function. 
 1 var Scene,
 2   __hasProp = {}.hasOwnProperty,
 3   __extends = function(child, parent) { for (var key in parent) { if (__hasProp.\
 4 call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructo\
 5 r = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); ch\
 6 ild.__super__ = parent.prototype; return child; };
 7 
 8 Scene = (function(_super) {
 9   __extends(Scene, _super);
10 
11   function Scene(bgColor) {
12     if (bgColor == null) {
13       bgColor = 'blue';
14     }
15     this.initialize();
16   }
17 
18   return Scene;
19 
20 })(cjs.Container);
Actually, all the CreateJS follows its own way to create the object inheritance structure. The following source code of the Shape class shows  how CreateJS inherits.
Shape.js, from CreateJS
 1 (function() {
 2   "use strict";
 3 
 4   var Shape = function(graphics) {
 5     this.initialize(graphics);
 6   };
 7   var p = Shape.prototype = new createjs.DisplayObject();
 8   Shape.prototype.constructor = Shape;
 9 
10   // public properties:
11 
12     p.graphics = null;
13 
14   // constructor:
15 
16     p.DisplayObject_initialize = p.initialize;
17 
18     p.initialize = function(graphics) {
19       this.DisplayObject_initialize();
20       this.graphics = graphics ? graphics : new createjs.Graphics();
21     };
22 
23 
24     p.isVisible = function() {
25       ...
26     };
27 
28 
29     p.DisplayObject_draw = p.draw;
30 
31 
32     p.draw = function(ctx, ignoreCache) {
33       ...
34     };
35 
36 
37     p.clone = function(recursive) {
38       ...
39     };
40 
41     p.toString = function() {
42       ...
43     };
44 
45   createjs.Shape = Shape;
46 }());
Exporting the class definition
We separate each part of code into its own file. The benefit of having separated files is that we can modularity logic into very specific domain. For every specific module, we only focus on its own logic. This helps making each parts less bugs.
It is a good practice that each file is separated. The compiled JavaScript of each files are put into an isolated function group by default. If we need to expose specific variables to other files, we can reference them to the global object under the app namespace.
this.exampleApp.Scene = Scene
Then we can reference the exported Class in another file.
Scene = this.exampleApp.Scene
4. Adding static stylish menu
In this step, we implement the menu scene and put menu item on it.
Time for Action
Let’s follow the steps to create the menu scene.
- We create the our menu scene. Let’s add the code to 
scenes.coffee.scenes.coffee
1classSceneAextendsScene2constructor:->3super('#EDE4D1')45header=newcjs.Bitmap'images/header.png'6header.scaleX=header.scaleY=0.57@addChildheader89info=newcjs.Bitmap'images/info.png'10info.y=35611info.scaleX=info.scaleY=0.512@addChildinfo1314photoA=newcjs.Bitmap'images/a.png'15photoA.y=3816photoA.scaleX=photoA.scaleY=0.517@addChildphotoA1819photoB=newcjs.Bitmap'images/b.png'20photoB.y=14621photoB.scaleX=photoB.scaleY=0.522@addChildphotoB2324photoC=newcjs.Bitmap'images/c.png'25photoC.y=25326photoC.scaleX=photoC.scaleY=0.527@addChildphotoC2829#exporttoglobalappscope30this.exampleApp.SceneA=SceneA
 - Make sure we import any newly created class into our App scope in order to use them.
    
app.coffee
1#alias2cjs=createjs3setting=this.exampleApp.setting4Scene=this.exampleApp.Scene5SceneA=this.exampleApp.SceneA
 - In the app logic, we replace the old 
Sceneby the newly createdSceneAclass.app.coffee
1classApp2constructor:->3...4sceneA=newSceneA()5@stage.addChildsceneA
 
What’s happening?
We created a new scene by inheriting the original Scene class definition. The inheritance allows us to define custom scene easily.
5. Displaying another scene after menu selection
In this step, we build a simple scene manager to control the presence of different scenes.
Preparation
Our scene management is inspired from the navigation controller in iOS. The navigation controller stores a stack of added scene. Developers that use this manager can push and pop scenes.
Time for Action
Let’s follow the steps to create our own scene manager for the app.
- We have more than 1 scene in our app. To make things easier, we design a scene manager that manage the scene displaying and leaving. we create a new file named 
scene-management.coffeefor this logic. Then put the following code into the newly created file.scene-management.coffee
1#aglobalappobject.2this.exampleApp?={}34#Anobjecttomanagescene,undertheappnamespace.5this.exampleApp.sceneManager={6stage:undefined7scenes:[]8lastScene:->@scenes[@scenes.length-1]9resetWithScene:(scene)->10@scenes.length=011@scenes.pushscene12@stage.addChildscene13popScene:->14@stage.removeChild@lastScene()15@scenes.pop()16@lastScene().mouseEnabled=true17pushScene:(scene)->18@lastScene().mouseEnabled=false19@scenes.pushscene20@stage.addChildscene21}
 - We create more scenes to test our example. Add the 
SceneBto thescenes.coffee.scenes.coffee
1classSceneBextendsScene2constructor:(contentId='a')->3super('white')45content=newcjs.Bitmap"images/page-view-content-#{contentId}.png"6content.scaleX=content.scaleY=0.57@addChildcontent89header=newcjs.Bitmap'images/header-back.png'10header.scaleX=header.scaleY=0.511@addChildheader1213header.on'click',->14sceneManager.popScene()
 - Then we create the 
SceneInfo.scenes.coffee
1classSceneInfoextendsScene2constructor:->3super('white')45content=newcjs.Bitmap"images/info-content.png"6content.scaleX=content.scaleY=0.57@addChildcontent89@on'click',->10sceneManager.popScene()
 - Make sure we export the newly defined class so that the 
App, which is in another file, can access to these classes.app.coffee
1#exporttoglobalappscope2this.exampleApp.SceneA=SceneA3this.exampleApp.SceneB=SceneB4this.exampleApp.SceneInfo=SceneInfo
 - In the 
scenes.coffeefile, we add the click event handling to the menu elements. Tapping the elemnets will lead to a new scene to display the image or the information scene.scenes.coffee
1sceneManager=this.exampleApp.sceneManager23info=newcjs.Bitmap'images/info.png'4info.y=3565info.scaleX=info.scaleY=0.56@addChildinfo7info.on'click',->8scene=newSceneInfo()9sceneManager.pushScenescene1011#Menuitem112photoA=newcjs.Bitmap'images/a.png'13photoA.y=3814photoA.scaleX=photoA.scaleY=0.515@addChildphotoA16photoA.on'click',->17scene=newSceneB('a')18sceneManager.pushScenescene1920#Menuitem221photoB=newcjs.Bitmap'images/b.png'22photoB.y=14623photoB.scaleX=photoB.scaleY=0.524@addChildphotoB25photoB.on'click',->26scene=newSceneB('b')27sceneManager.pushScenescene2829#Menuitem330photoC=newcjs.Bitmap'images/c.png'31photoC.y=25332photoC.scaleX=photoC.scaleY=0.533@addChildphotoC34photoC.on'click',->35scene=newSceneB('c')36sceneManager.pushScenescene
 - We have created a few new scenes. Make sure we have aliased these new classes in the 
app.coffeefile.app.coffee
1#alias2cjs=createjs3setting=this.exampleApp.setting4sceneManager=this.exampleApp.sceneManager5SceneA=this.exampleApp.SceneA6SceneB=this.exampleApp.SceneB7SceneInfo=this.exampleApp.SceneInfo
 - In the main 
Applogic, We removed the old Scene creation logic and make use of thesceneManagerto handle the scene visualization.app.coffee
1classApp2constructor:->3...45sceneA=newSceneA()6@stage.addChildsceneA78sceneManager.stage=@stage910scene=newSceneA()11sceneManager.resetWithScenescene
 - We created new files so we need to include the files in the Gulpfile compiling pipeline.
    
Gulpfile.coffee
1gulp.task'js',->2gulp.src[3'./app/scripts/setting.coffee'4'./app/scripts/scene-manager.coffee'5'./app/scripts/scenes.coffee'6'./app/scripts/app.coffee'7]8.pipecoffee()9.pipeconcat'app.js'10.pipegulp.dest'./app/scripts/'
 
What’s happening?
The scene manager is an object without class definition. We put it on the exampleApp namespace to let other modules access it.
There are 2 properties, stage and scenes. The stage is refer to the target container that holds the scenes. The scenes is an array of the scenes we have added to the stage.
Then we defined 3 essential methods, resetScene, pushScene and popScene, and 1 helper method, lastScene.
The resetWithScene clears the scenes array to provide a clean state. Then it add the give scene as the first scene, as known as root scene in such kind of navigation pattern.
The pushScene takes the given new scene object and add to the scenes stack. Then it displays the new added scene to the screen.
The popScene, on the other hand, remove the last scene from the screen and from the scenes stack. That’s why we have a helper method that returns the last scene.
6. Animating transition between scenes
In this step, we make use of the exported Flash animation to build the animated transition effect.
Preparation
Before we begin, make sure we have the transitions.js file ready in the scripts folder. We include the file into the index.html before loading our main App logic.
index.html
1 ...
2   <script src="http://code.createjs.com/easeljs-0.7.1.min.js"></script>
3   <script src="http://code.createjs.com/tweenjs-0.5.1.min.js"></script>
4   <script src="http://code.createjs.com/movieclip-0.7.1.min.js"></script>
5   <script src="scripts/transitions.js"></script>
6   <script src="scripts/app.js"></script>
7 </body>
       
 | 
    
       If you have modified the animation in Flash, you need to publish the Flash document again to update the JavaScript file.  | 
  
Time for Action
Let’s work on the following steps to add the animated transition to the app.
- In the 
scene-manager.coffee, we add one new methodpushSceneWithTransitionwhich add the animated transition while switching scenes.scene-manager.coffee
1this.exampleApp.sceneManager={2...3pushSceneWithTransition:(scene,transitionClassName)->4transition=newlib[transitionClassName]()5transition.x=setting.width/26transition.y=setting.height/278scene.visible=false910@pushScenescene1112#ThetransitionanimationinFlashshoulddispatch`sceneShouldChange`eve\13nt.14transition.on'sceneShouldChange',->15scene.visible=true1617@stage.addChildtransition18}
 - In the 
scenes.coffee, we change to use the newpushSceneWithTransitionmethod.class SceneA extends Scene constructor: ->
1...2info.on'click',->3scene=newSceneInfo()4sceneManager.pushSceneWithTransitionscene,'TransitionAnimationA'56...7photoA.on'click',->8scene=newSceneB('a')9sceneManager.pushSceneWithTransitionscene,'TransitionAnimationB'1011...12photoB.on'click',->13scene=newSceneB('b')14sceneManager.pushSceneWithTransitionscene,'TransitionAnimationB'1516...17photoC.on'click',->18scene=newSceneB('c')19sceneManager.pushSceneWithTransitionscene,'TransitionAnimationB' 
What’s happening?
We have added a custom animated transition when we switch scene in the app.
Adding the generated transition
Any exported Flash movieclip is put into a lib namespace. For example, if the movieclip name is AnimatedBall, we can create an instance by using new lib.AnimatedBall().
In our code, the transition class name is a variable. By using the array notation instead of dot notation, we can create new instance of a class where the class name is variable.
new lib[transitionClassName]()
Custom event: sceneShouldChange
In the scene manager, we listen to the sceneShouldChange event and toggle the new scene’s visibility. 
1 transition.on 'sceneShouldChange', ->
2   scene.visible = true
This relies on the Flash animation which dispatches the event at the middle of the transition animation.

sceneShouldChange event in the Flash timeline
In the screenshot, you will find an action is defined in the middle of the transition animation. When the animation reaches this frame, it dispatch the event. We capture this custom event in the scene manager to actually switch the scene.
7. Optimizing for retina display
We may find the app looks blurry when we test the web app in iPhone or Android device with high-definition display. That’s because the retina display trys to render the graphics by doubling our pixels. In this step, we optimize the canvas rendering in retina display.
Time for Action
Let’s add the retinalize utility via the following steps.
- The 
retinalizemethod is kind of utility that’s independent to our logic. We create a new fileutility.coffeeand place the following code inside it.utility.coffee
1retinalize =(canvas, stage) ->2# We skip the logic if the device is not retina3# or it doesn’t support the pixel ratio4returnif(window.devicePixelRatio)56# cache the pixel ratio7ratio =window.devicePixelRatio89# get the original canvas dimension10height =canvas.getAttribute('height')11width =canvas.getAttribute('width')1213# set the new dimension with ratio multiplication14canvas.setAttribute('width',Math.round(width*ratio))15canvas.setAttribute('height',Math.round(height*ratio))1617# ensure the canvas CSS style follows the original dimension18canvas.style.width =width+"px"19canvas.style.height =height+"px"2021# scale the entire stage so we can use the original coordinate in our app.22stage.scaleX = stage.scaleY =ratio
 - We can then call the retinalize method after we initialize the canvas and stage variable.
    
app.coffee
1classApp2#Entrypoint.3constructor:->4console.log"Welcome to my portfolio."5@canvas=document.getElementById("app-canvas")6@stage=newcjs.Stage(@canvas)78window.utility.retinalize(@canvas,@stage)
 - We have created a new file. As usual, we include the new file in our compiling pipeline. Add the following highlighted line to the 
Gulpfile.coffeeGulpfile.coffee
1gulp.task'js',->2gulp.src[3'./app/scripts/setting.coffee'4'./app/scripts/retinalize.coffee'5'./app/scripts/scene-manager.coffee'6'./app/scripts/scenes.coffee'7'./app/scripts/app.coffee'8]9.pipecoffee()10.pipeconcat'app.js'11.pipegulp.dest'./app/scripts/'
 
What’s happening?
When the browser detects the display has a higher devicePixelRatio, which means for every ‘point’ of the display, it renders more than 1 pixels. For such types of display, we enlarge the canvas content while keeping the dimension of the  <canvas> element unchanged. This allows the retina display to render the graphics in its native pixel resolution, and hence make the canvas graphics looks sharp.
       
 | 
    
       It’s worth noting that the retina display not only applies to mobile device but also desktops, such as Mackbook Pro Retina and the 5K retina iMac.  | 
  
Further challenges
There are some essential features we haven’t implemented in the example app.
For example, we don’t have scrolling in the menu scene so we can’t display more photos.
Project 1B – DOM-based app with animated transitions
In this project, we improve the Project 1 to make all the content accessible by the browsers.
Mission Checklist
We are going to replace the canvas-based content into DOM elements.
- Defining DOM elements
 - Controlling page-based scenes.
 - Positioning the canvas-based transition to fit window size.
 - Falling back for old browser without canvas support.
 - Fine tuning the app styles.
 - Supporting retina display.
 
Project Preparation
In the project, we will need the updated image assets. You may download them via the following URL.
http://mak.la/cjs-proj1b-images.zip
1. Defining DOM elements
Time for Action
- In our 
index.htmlwe had the canvas inside the#appDIV. We will add new DOM elements after this canvas tag, for each page of content.index.html
1<divid="app">2<canvasid="app-canvas"width="300"height="400"></canvas>3<!--Wewilladdeachpageofcontentfromhere-->4</div>
 - First, we add the main menu page. Add the following 
#mainDIV after our canvas.index.html
1<divid="app">2<canvasid="app-canvas"width="300"height="400"></canvas>3<divid="main"class="page">4<divclass='header'>5<imgsrc="images/header.png"alt="Header">6</div>7<ulid="main-list"class='non-collapse-content'>8<li><ahref="#detail-page"><imgsrc='images/a.png'alt='Photo A'></a></li>9<li><ahref="#detail-page"><imgsrc='images/b.png'alt='Photo B'></a></li>10<li><ahref="#detail-page"><imgsrc='images/c.png'alt='Photo C'></a></li>11<!--Feelfreetoaddmorelistitemshere-->12</ul>13<divid="info-link">14<ahref="#info-page"data-transition="TransitionAnimationA"><imgsrc='ima\15ges/info.png'alt='Link to Info'></a>16</div>17</div>18</div>
 - Next, we add the 
#detail-pageafter the mail page. You may add other page if needed.index.html
1<divid="app">2<canvasid="app-canvas"width="300"height="400"></canvas>3<divid="main"class="page">...</div>4<divid="detail-page"class="page">5<ahref="#"class='header'><imgsrc='images/header-back.png'alt='Back to m\6ain'></a>7<imgsrc='images/photo-a.png'alt='Detail of Photo A'>8<p>HereisaphotofromUnsplash.Thephotoisfreeforcommercialuse.Ip\9utitherejustfortheappexample.ThephotowastakenbyBenMoore.</p>10<divid="info-link">11<ahref="#info-page"data-transition="TransitionAnimationA"><imgsrc='ima\12ges/info.png'alt='Link to Info'></a>13</div>14</div>15</div>
 - Finally, we add the 
#info-page. Please note that we have replaced the text image into real text.index.html
1<divid="app">2<canvasid="app-canvas"width="300"height="400"></canvas>3<divid="main"class="page">...</div>4<divid="detail-page"class="page">...</div>5<divid="info-page"class="page">6<ahref="#"class='header'><imgsrc='images/header-back.png'alt='Back to m\7ain'></a>8<divclass='non-collapse-content'>9<p>Thisisanexampleappthatservesasthe1stchapterofmybook–Ric\10hInteractiveAppDevelopmentwithCreateJS.Thisexampledemonstratesacustom\11animatedtransition.Itlackssomeessentialfeaturesbutthisisjustforthec\12hapter1.Morefeaturescominginfuturechapter.</p>13<p>ThisexampleisboughttoyoubyMakzan.Hehaswrittenthreebooksan\14donevideocourseonbuildingaFlashvirtualworldandcreatinggameswithHTM\15L5andthelatestwebstandards.HeiscurrentlyteachingcoursesinHongKonga\16ndMacaoSAR.</p>17</div>18</div>19</div>
 - Before we move on, we add some basic styles. It replaces the CSS file from project 1.
    
app.css
1ul{2list-style:none;3}45img{6width:100%;7border:0;8}910/* canvases sit inside the #app frame. It’s similar to layers. */11#app{12position:relative;13}14#app>canvas{15position:fixed;16display:none;/* default hide until we use it */17z-index:999;18}
 
What’s happening?
We are building the project of Jack Portfolio from scratch. We use DIV with .page class to indicate one page of content. They are all added into #app element.
Here is the new #app DOM structure.
1 <div id="app">
2   <canvas id="app-canvas" width="300" height="400"></canvas>
3   <div id="main" class="page">...</div>
4   <div id="detail-page" class="page">...</div>    
5   <div id="info-page" class="page">...</div>
6 </div>
The HTML is designed to be used without any JavaScript and fancy transition effect. All the links between content are based on the hash anchors. User can still view and link to different part of the content with neither canvas support nor JavaScript support.
2. Page Transition Manager
In this step, we control our .page DOM elements by a PageManager.
Preparation
We need to modify the scenes management. It was controlling the DisplayObject in CreateJS canvas. Now we need to control the DOM elements. I changed the class from SceneManager to PageManager to make the code less confusing. The structure will be the same as what we had in canvas-based scenes manager.
 1 class app.PageManager
 2   constructor: (@stage)->    
 3     # init the pages    
 4   lastScene: -> 
 5     # return last scene
 6   resetWithScene: (scene) ->
 7     # reset scene
 8   popScene: ->    
 9     # remove the last scene
10   pushScene: (scene)->
11     # show the given scene    
12   pushSceneWithTransition: (scene, transitionClassName) ->
13     # show the given scene with transition
Time for Action
- First, we work on the CSS. Each page is absolute positioned
    
app.css
1/* canvases sit inside the #app frame. It’s similar to layers. */2#app{3position:relative;4}5#app>canvas{6position:fixed;7display:none;/* default hide until we use it */8z-index:999;9}1011/* Page related */12.page{13width:100%;14height:100%;15position:absolute;16}
 - By default, we hide all the 
.page. The first page will be added from theresetWithScenemethod.page-manager.coffee
1constructor:(@stage)->2@scenes=[]3$('.page').hide()45#registerclicksonallpages6$('a[href^="#"]').click(event)=>7pageId=$(event.currentTarget).attr('href')89#whenit's link to #, it is a back transition10pageId = '.page:first' if pageId == '#'1112transition = $(event.currentTarget).data('transition')1314@pushSceneWithTransition$(pageId),transition
 - The 
lastScenereturns the last element of thescenesarray.page-manager.coffee
1lastScene:->@scenes[@scenes.length-1]
 - In the 
resetScenemethod, we reset the scenes array and use jQuery to show the given scene.page-manager.coffee
1resetWithScene:(scene)->2@scenes.length=03@scenes.pushscene45$(scene).show()
 - When we remove scene, we don’t actually remove the scene like we did in canvas. Instead, we hide the DOM element of the scene.
    
page-manager.coffee
1popScene:->2$(scene).hide()3@scenes.pop()
 - In DOM elements, we control the DOM’s visibility. So pushing a scene means we hide the last one and show the given one. The browser may be scrolled in the last scene, that’s why we reset the scroll position.
    
page-manager.coffee
1pushScene:(scene)->2$(@lastScene()).hide()3@scenes.pushscene4$(scene).show()56#resetthescroll7$(window).scrollTop(0)
 - Here comes the core part, transition. The transition is still controlled by canvas. During the control of the DOM visibility, we show the canvas animation and hide the canvas after the transition completed.
    
pushSceneWithTransition: (scene, transitionClassName=’TransitionAnimationB’) -> transition = new libtransitionClassName
1#ThedemensionfollowstheFlashcanvasdimension2transition.x=300/23transition.y=400/245$('#app-canvas').show()67transition.on'sceneShouldChange',=>8@pushScenescene910transition.on'transitionEnded',->11$('#app-canvas').hide()1213@stage.addChildtransition - Let’s make use of the page transition. We change the 
app.coffeeto the following code.app.coffee
1#aglobalappobject.2this.exampleApp?={}34#alias5cjs=createjs6setting=this.exampleApp.setting7app=this.exampleApp89classApp10#Entrypoint.11constructor:->12console.log"Welcome to my portfolio."1314@canvas=document.getElementById("app-canvas")15@stage=newcjs.Stage(@canvas)1617cjs.Ticker.setFPS6018cjs.Ticker.addEventListener"tick",@stage#makesurethestagerefreshdr\19awingforeveryframe.2021app.sceneManager=newapp.PageManager(@stage)22app.sceneManager.resetWithScene$('.page:first')2324newApp()
 
What’s happening?
We created the page transition manager that is very similar to our previous scene manager. The only difference is that page manager handles .page DOM element and scene manager handles CreateJS container.
Improvement
We have reset the scroll position during scenes transition. In future, we may store the scoll position of each scene, so that we can resume the previous scroll position when popping to the last scene.
3. Positioning the canvas transition
Time for Action
- In the 
retinalize.coffeefile, we add asetFullScreenfunction that scales the canvas to fit the window dimension.retinalize.coffee
1#aglobalappobject.2this.exampleApp?={}34setting=this.exampleApp.setting56this.utility?={}78this.utility.setFullScreen=(canvas,stage)->9canvas.setAttribute'width',$(window).width()10canvas.setAttribute'height',$(window).height()11setting.width=$(window).width()12setting.height=$(window).height()1314#300istheoriginalFlashcanvaswidth15stage.scaleX=stage.scaleY=setting.width/300
 - In our app’s entry point, we invoke the 
setFullScreenfunction for the first time and register it to be run every time when the window resizes.app.coffee
1classApp2#Entrypoint.3constructor:->4console.log"Welcome to my portfolio."56@canvas=document.getElementById("app-canvas")7@stage=newcjs.Stage(@canvas)89utility.setFullScreen(@canvas,@stage)1011window.onresize==>12utility.setFullScreen(@canvas,@stage)1314...
 
4. Falling back in old browser
Time for Action
- We add a new file 
old-browser.jsto fall back the logic to basic HTML anchors navigation when the reader’s browser doesn’t support Canvas.old-browser.js
1(function(){2// Check if canvas is supported3isCanvas2DSupported=!!window.CanvasRenderingContext2D;45// Give up all logic6if(!isCanvas2DSupported){7// remove .page styles8$('.page').removeClass();9}10}).call(this);
 - We include the 
old-browser.jsfile right after the loading of jQuery and before loading our logic.index.html
1<scriptsrc="scripts/old-browser.js"></script>
 
What’s happening?
We check if the browser supports the canvas. When the browser is too old to run canvas, we fall back to the step-1 which presents the content via browser hash link. This is done by removing all the .page class to force the scene transition and the .page styles not working.
5. Fine tuning the styles
Time for Action
- In the 
app.css, we add some more styles to make the app looks nice.app.css
1#info-link{2position:fixed;3bottom:-5px;4left:0;5width:100%;6}78.header{9position:fixed;10top:0;11left:0;12width:100%;13}1415.non-collapse-content{16margin-top:25%;17}1819p{20padding:1rem;21}2223#main-list{24padding-bottom:100px;25}26#main-listli{27margin-top:-11%;28}
 
6. Supporting retina display
Preparation
Make sure you have downloaded the new images assets that contain the @2x version of the images.
http://mak.la/cjs-proj1b-images.zip
Time for Action
- In the 
index.html, We add thesrcsetattribute for every img tag.index.html
1<imgsrc="images/header.png"srcset='images/header.png 1x, images/header@2x.png\22x'alt="Header">3...4<li><ahref="#detail-page"><imgsrc='images/a.png'srcset='images/a.png 1x, ima\5ges/a@2x.png 2x'alt='Photo A'></a></li>6<li><ahref="#detail-page"><imgsrc='images/b.png'srcset='images/b.png 1x, ima\7ges/b@2x.png 2x'alt='Photo B'></a></li>8<li><ahref="#detail-page"><imgsrc='images/c.png'srcset='images/c.png 1x, ima\9ges/c@2x.png 2x'alt='Photo C'></a></li>10...11<imgsrc='images/info.png'srcset='images/info.png 1x, images/info@2x.png 2x'a\12lt='Link to Info'>13...14<imgsrc='images/header-back.png'srcset='images/header-back.png 1x, images/hea\15der-back@2x.png 2x'alt='Back to main'>
 
What’s happening?
srcset allows us to define separated images sources for different screen density.
Here is the syntax.
1 <img src='DEFAULT_PATH' src='FILE_PATH 1x, FILE_PATH 2x, FILE_PATH 3x' alt=''>
Project 2 – Rain or Not?
In this project, we create an app that helps the user knows if they need to begin the umbrella each day. It serves one single purpose which fetches the weather. In this example, we only care about the rainy day and the sunny say. In the future, you may modify it to support more weather conditions such as windy and snow days.
Before we get started into the project, here is the screenshot of whatwe are going to build. You can also try the project demo with the following link. I recommend you go try it so you can map the code with the app that we are building.

Screenshot of the app example
http://mak.la/demo-rain-or-not
Why this project is awesome
We have created an information based app in last example. In this example, we will explore how we can separate the logic into data, view and controlling logic. This is known as MVC, modal-view-controller.
We try to separate the logic, data and the view. This ensues that each module of code is minimal and so they are easy to maintain.
Mission Checklist
We are going to replace the canvas-based content into DOM elements.
- Setup the project
 - Data
 - Mocking API
 - View
 - Adding Canvas
 - Moving the Canvas in
 - Aligning with FlexBox
 - Device Rotation
 
The core logic of the app
1 ;(function($){
2 $.getJSON('http://api.openweathermap.org/data/2.5/weather?q=Macao,MO&callback=?'\
3 , function(data){
4     console.log(data);
5   });
6 }).call(this, jQuery);
1. Setup the project
Time for Action
- The 
index.htmlfile.index.html
1<!DOCTYPEhtml>2<htmllang='en'>3<head>4<metacharset='utf-8'>5<metaname='viewport'content='width=device-width, initial-scale=1'>6<metaname='apple-mobile-web-app-capable'content='yes'>7<title>RainorNot</title>8<linkrel='stylesheet'href='styles/app.css'>9</head>10<body>11<divid='app'>12<divid='main'class='page loading'>13<header>Macao</header>14<canvasid='app-canvas'class='out'width='300'height='300'>15<!--fallbackcontent-->16<imgclass='rainy-only status'src='http://placehold.it/300x300&text=\17rainy'alt='rainy'>18<imgclass='sunny-only status'src='http://placehold.it/300x300&text=\19sunny'alt='sunny'>20</canvas>21<pclass='description rainy-only'>Bringyourumbrella</p>22<pclass='description sunny-only'>Haveaniceday!</p>23</div>24</div>2526<scriptsrc='//code.jquery.com/jquery.min.js'></script>27<scriptsrc='//code.createjs.com/easeljs-0.7.1.min.js'></script>28<scriptsrc='//code.createjs.com/tweenjs-0.5.1.min.js'></script>29<scriptsrc='//code.createjs.com/movieclip-0.7.1.min.js'></script>30<scriptsrc='scripts/rain-or-not-lib.js'></script>31<scriptsrc='scripts/app.js'></script>32<scriptsrc='//cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.m\33in.js'></script>34</body>35</html>
 - The 
app.coffeefile.app.coffee
1this.rainOrNot={}23classApp4constructor:->5console.log"Do you need your umbrella today?"67@refresh()8910refresh:->11data=newData()12view=newView()13data.fetch(is_rainy)->14view.update(is_rainy)
 - Make sure we invoke the App:
    
app.coffee
1newApp()
 
What just happened?
2. Data module
The data logic is responsible to fetch the data from the source and parse the data.
Time for Action
- We create a new class for the Data module.
    
app.coffee
1classData2constructor:->3@api='http://api.openweathermap.org/data/2.5/weather?q=Macao,MO'4fetch:(callback)->56$.getJSON@api,(data)->7console.log(data)89code=data.weather[0].id+""#forcetostring1011#rainycodeallstartat512ifcode[0]=='5'13callback(true)14else15callback(false)
 
3. Mocking API
We need to use different API response to test our logic.
Mocking an API usually means that we create a static JSON file and put it somewhere that the development app can access. But this could be more automatic if we have lots of mock API to create. A tool named mockable comes to help.
I created 2 canned responses, sunny and rainy, with the following URL. The response JSON is copied from the source, OpenWeather.
1 http://demo5385708.mockable.io/weather?sunny
2 http://demo5385708.mockable.io/weather?rainy
Time for Action
- In the 
Dataclass, we add a mock API to test different API response. Add the following code to override the API url.app.coffee
1classData2constructor:->3@api='http://api.openweathermap.org/data/2.5/weather?q=Macao,MO'45#mock6@api='http://demo5385708.mockable.io/weather?rainy'7...
 
4. View
Defining the CSS styles.
 1 .loading {
 2   background-image: url(../images/loading.png) center center no-repeat;
 3 }
 4 
 5 .sunny {
 6   background: #B8DCF1
 7 }
 8 .rainy {
 9   background: #9FB6C4;
10 }
The main view that controls DOM elements and the more-specific View objects, such as Background and CanvasView
 1 class View
 2   constructor: ->
 3     $('.status').hide()
 4     $('.description').hide()
 5 
 6     @canvasView = new CanvasView()
 7     @canvasView.reset()
 8     @background = new Background()
 9   update: (is_rainy=true)->
10     $('.loading').removeClass('loading')
11     @canvasView.moveIn()
12     if is_rainy
13       $('.rainy-only').show()
14       $('.sunny-only').hide()
15       @canvasView.showRainy()
16       @background.setRainyBackground()
17     else
18       $('.rainy-only').hide()
19       $('.sunny-only').show()
20       @canvasView.showSunny()
21       @background.setSunnyBackground()
Controlling the Background
1 class Background
2   constructor: ->
3     @element = $('body')
4   setSunnyBackground: -> @element.addClass('sunny')
5   setRainyBackground: -> @element.addClass('rainy')
5. CanvasView
 1 class CanvasView
 2   constructor: ->
 3     cjs = createjs
 4     @canvas = document.getElementById("app-canvas")
 5     @stage = new cjs.Stage(@canvas)
 6 
 7     cjs.Ticker.setFPS 60
 8     cjs.Ticker.addEventListener "tick", @stage
 9     cjs.Ticker.addEventListener "tick", @tick
10 
11     @retinalize()
12   tick: =>
13     @applyDeviceRotation()
14 
15   retinalize: ->
16     CanvasView.width ?= @canvas.width
17     CanvasView.height ?= @canvas.height
18 
19     @canvas.style.width = CanvasView.width + 'px'
20     @canvas.style.height = CanvasView.height + 'px'
21     @canvas.width = CanvasView.width * 2
22     @canvas.height = CanvasView.height * 2
23     @stage.scaleX = @stage.scaleY = 2
24 
25   moveIn: -> $(@canvas).removeClass('out').addClass('in')
26   reset: -> $(@canvas).removeClass().addClass('out')
27 
28   showRainy: ->
29     @icon = new lib.Rainy()
30     @stage.addChild @icon
31   showSunny: ->
32     @icon = new lib.Sunny()
33     @stage.addChild @icon
6. Moving the canvas in
Time for Action
This is a subtle effect that we move the canvas icon from bottom to the center of the viewport by using CSS3 transition.
app.css file
 1 .out {
 2   transform: translateY(100%);
 3   opacity: 0;
 4 }
 5 
 6 .in {
 7   transition: all .75s cubic-bezier(0.140, 0.460, 0.160, 1.210);
 8   transform: translateY(0%);
 9   opacity: 1;
10 }
The transition may fail if the data is cached without loading. We need to add a little delay to make the CSS transition work.
1 class App
2   constructor: ->
3     console.log "Do you need your umbrella today?"
4 
5     setTimeout =>
6       @refresh()
7     , 500
7. Aligning with FlexBox
Flexbox is the next hot topic on layout. It’s draft was first published in 2009 and has reach a relatively stable status now, after 5 years of discussions and name changes.
In the step, we will make the app frame a flexbox container and layout our user interface elements at the center of the screen, vertically.
Time for action
- Add the following to the CSS to make the items align center vertically.
    
app.css
1/* Flexbox */2html,body,#app,.page{3height:100%;4}5.page{6flex-direction:column;7display:flex;8align-items:center;9justify-content:center;10}
 
I recommend reading the full guide to FlexBox. Or you may check the latest working draft on W3C website.
http://www.w3.org/TR/css-flexbox-1/
8. Device Rotation
In this step, we will add a motion effect where the rainy and sunny icon slightly moves based on the device rotation.
This is done by listening to the DeviceOrientationEvent and use the rotation degree to control the icon.
Time for Action
Let’s work on the following steps to create a motion effects based on the rotation degree of the device.
- We create a new class which keep storing the latest device rotation. The value is from the Gyroscope where axises are represented in 
alpha,beta,gamma.app.coffee
1classDeviceRotation2constructor:->3DeviceRotation.a=DeviceRotation.b=DeviceRotation.g=045#gyroscope6$(window).on'deviceorientation',(e)->7DeviceRotation.a=@a=e.originalEvent.alpha8DeviceRotation.b=@b=e.originalEvent.beta9DeviceRotation.g=@g=e.originalEvent.gamma10$('#debug').text("#{@a} #{@b} #{@g}")
 - We need to apply the value. In the 
CanvasViewclass, we use the device rotation to offset the sunny and rainy icons.app.coffee
1applyDeviceRotation:->2a=DeviceRotation.a3b=DeviceRotation.b4g=DeviceRotation.g56@icon.front.x=CanvasView.width/2+g/107@icon.front.y=CanvasView.height/2+b/1089@icon.back.x=CanvasView.width/2+g/510@icon.back.y=CanvasView.height/2+b/5
 - Now the App constructor becomes: 
    
app.coffee
1classApp2constructor:->3console.log"Do you need your umbrella today?"45setTimeout=>6@refresh()7,50089$('body').click=>@refresh()1011newDeviceRotation()
 
What happened?
The #debug element is used to observe the value of the gyroscope. By printing out the values, we can hold the device on hand, and then rotate the device into different directions and observe how the tilting changes the three rotation axises. After we get what we need from the numbers, we can hide it or even delete it. Make sure these debug information is not visible when we deploy the app in production environment.
Device Orientation Event
For more information on using the 3 axises of the device rotation, please check the Apple Developer documentation:
http://mak.la/apple-device-orientation-event
I created a utility that inspects the device rotation value and prints them out nicely.
https://play.google.com/store/apps/details?id=net.makzan.gyroinspcetor
You may use this tool to inspect the value you want by holding the device at the target rotation.
Controlling Flash instance
When we set an instance name in the Flash movie clip which being exported to canvas, we can actually access that instance from the Javascript. This gives a huge convenient way to manipulated with the exported graphics.
For example, in this example, we listen to the drive rotating events and changes the movement offset of both the front and back instance of the weather symbol. The front and back instance are already defined in the flash and exported to the Javascript.
Summary
In this project, we learnt to define our logic into different modules for easier maintenance. The logic is divided into data querying, view rendering and controller that bridges between data and view.
We also learned to control pre-defined symbol instances that was exported from Adobe Flash.
At last, we listen to the device orientation to get the 3 axis value of device rotation.
Project 3 – Solar System
In this project, we are going to create a tool that presents our solar system.
The system is constructed by HTML DOM elements and CSS transform and transition.
Why is this project awesome?
By following the project steps, you will learn how to scroll the view in a parallax effect. This effect created an illusion of depth. That’s based on our real world that’s in perspective view. Things aren’t look the same at different distances. Things that’s far away from us looks smaller and moves slower to us. By creating the same scaling and movement at different layers, we can create similar depth feeling.
Preparation
Before we get started, we want to include one more Gulp plugin.
If you are using an existing project example, you may run the following command to add the gulp-sass compiler to the project.
1 npm install gulp-sass --save-dev
If you are working on a new folder, you may download the following skeleton which contains the basic project file structure and the package.json file. 
1 http://mak.la/cjs-skeleton
Make sure to run npm install --save-dev after you download the skeleton code.
SCSS is a kind of CSS preprocessor. Preprocessor means that we write another syntax to express our css rules and then compile it into css. The beauty of scss is that it is like css. If you don’t use any of the scss specific syntax, the code is actually valid css file.
HTML
This is the HTML skeleton.
 1 <div id='app'> 
 2   <div id='solar-system' class='focus-earth'>
 3     <div class='layer deep-bg'></div>
 4     <div class='layer bg'></div>
 5     <div class='layer planets'>
 6       <!-- each planet here -->
 7     </div>
 8   </div>
 9   <div class='info-panel'>
10     <!-- Info text here -->
11   </div>
12   <div class='buttons'>      
13     <!-- button to focus on for every planet -->
14   </div>
15   
16   <div class='detail-panel out'>
17     <!-- pop up panel with detail information on every planet -->
18   </div>
19 </div>
Almost the elements are position absolute. The #app element is relative position to act as the relative coordinate for all the absolute position child elements. They all overlap together. Then we adjust the transform and too left bottom right to control their position.
       
 | 
    
       We use absolute position when we want the layers (groups) to overlap together.  | 
  
The solar system had three layers. The deep background, background and the planets. The two background layers is essential to create a space movement illusion.
       
 | 
    
       In the futuron, there may be additional layers such as the sun and a front layer of dust.  | 
  
Basic CSS:
 1 * {
 2   box-sizing: border-box;
 3 }
 4 
 5 body, div, ul, li, h1, h2, h3, p {
 6   margin: 0;
 7   padding: 0;
 8 }
 9 
10 img {
11   max-width: 100%;
12 }
13 
14 html, body, #app, #solar-system {
15   width: 100%;
16   height: 100%;
17 }
18 
19 
20 #app {
21   position: relative;  
22   overflow: hidden;
23 }
Planet buttons
HTML:
 1 <div class='buttons'>
 2     <a href='#mercury' class='focus-button'>Mercury</a>
 3     <a href='#venus' class='focus-button'>Venus</a>
 4     <a href='#earth' class='focus-button'>Earth</a>
 5     <a href='#mars' class='focus-button'>Mars</a>
 6     <a href='#jupiter' class='focus-button'>Jupiter</a>
 7     <a href='#saturn' class='focus-button'>Saturn</a>
 8     <a href='#uranus' class='focus-button'>Uranus</a>
 9     <a href='#neptune' class='focus-button'>Neptune</a>
10   </div>
CSS:
 1 /* User Inteface */
 2 .buttons {
 3   position: absolute;
 4   height: 100%;
 5   right: 0;
 6   
 7   display: flex;
 8   flex-direction: column-reverse;
 9   
10   a {
11     display: block;
12     flex: 1;
13     
14     text-decoration: none;
15     color: #555;
16     
17     line-height: 50px;
18     
19     &.active {
20       color: white;
21     }
22   }
23 }
The button is laid out using the flexbox to ensure they are evenly distributed.
The nested scope in the preprocessor allows us to group related styles together. We can modularize the styles to specific part of the app.
The solar system
 1 $space-height: 13000px;
 2 $bg-z: 10;
 3 $deep-bg-z: 30;
 4 
 5 #solar-system {    
 6   position: absolute;
 7   overflow: hidden;  
 8   
 9   transform-origin: 0 0;
10 }
11 
12 
13 .layer {
14   position: absolute;
15   transition: all 1.5s ease-out;
16 }
17 
18 .deep-bg {
19   width: 100%;
20   height: $space-height;
21   background: black url(../images/deep-bg.jpg);
22 }
23 
24 .bg {
25   width: 100%;
26   height: $space-height;
27   background: url(../images/bg.jpg);
28   opacity: .3;
29 }
30 
31 .planet {
32   position: absolute;   
33   
34   img {
35     max-width: 80px;  
36   }
37 }
Those preprocessors usually allow us to define variable and expressions. For example, we defined several variables for our solar system. They are the height of the space view. The virtual Z index of the layers.
We don’t need the height of the space view if we order the planets from top to bottom.
We need the height because we are calculating the position from the bottom.
Showing the planets
The value of the planet is based on the average distance the real planet are away from the sun. We have a multiplier to adjust the scale.
 1 $planets: "mercury" "venus" "earth" "mars" "jupiter" "saturn" "uranus" "neptune";
 2 $distances:  57*2px 108*2px 150*2px 228*2px 779*2px 1430*2px 2880*2px 4500*2px;  
 3 $x-positions: 30vw 15vw 45vw 50vw 35vw 60vw 25vw 56vw;
 4 
 5 @for $i from 1 through length($planets) {
 6   $name: nth($planets, $i);
 7   $distance: nth($distances, $i);
 8   $x-position: nth($x-positions, $i);
 9   .#{$name} {
10     transform: translateX($x-position) translateY(calc(#{$space-height} - #{$dis\
11 tance}));  
12   }
13 
14   ...
15 }
The looping expressions allows us to define similar css rules without explicitly typing all of them. For example, we use loop and a list to iterate all 8 planets rules and their styles when focused.
Scss list is useful when we cant express the calculation with just the looping index. For example, we want to iterate the name of the 8 planets, so we use a list to store the planet name and use it to define the class based on these names.
You may think that list is kind of array in programing language. Actually scss even has a nested list like a 2 dimension array.
There are some utility functions to help us manipulate the scss list. The most common one is the nth function where we get the value based on the index, usually within an for loop. The length function helps us to define the ending condition if the for loop.
The calc function in css allows is to express a calculation as a property value.
       
 | 
    
       Please note that css does support using expression as property value, it is the calc function. And css also supports variable. But variable are supported in Firefox only right now. So we have to mix the scss variable and the css calc. By mixing them, we need to use the   | 
  
 1 @for $i from 1 through length($planets) {
 2   $name: nth($planets, $i);
 3   $distance: nth($distances, $i);
 4 
 5   ...
 6 
 7   #solar-system.focus-#{$name} {
 8     .planets.layer {
 9       transform: translateY(calc(-#{$space-height} + #{$distance} + 40vh));    
10     }
11     .bg.layer{
12       transform: translateY(calc((-#{$space-height} + #{$distance} + 40vh) / #{$\
13 bg-z}));
14     }
15     .deep-bg.layer{
16       transform: translateY(calc((-#{$space-height} + #{$distance} + 40vh) / #{$\
17 deep-bg-z}));
18     }
19   }
20 }
The focus style explicitly moves the 3 layers into different transform Y based on the calculation.
By toggling different focus styles on different planets, the 3 layers move in different speeds and thus created the parallax effect.
Detail Panel
1 <div class='detail-panel out'>
2   <div id='tab-mercury' class='tab'>          
3     <h1>Mercury</h1>
4     <img src='images/mercury.jpg' alt='Mercury'>
5     <p><small>Photo by NASA. Public Domain.</small></p>
6     <p><small>Tap anywhere to close</small></p>
7   </div>
8   <!-- other planets’ detail go here -->
9 </div>
Project 4 – Countries Area
In this project, we create an app that draws a chart by using canvas and CreateJS.
This example demonstrated both multiple select list and single item select list.
The multiple select list is done via check box with array as name. The single item selection list is done by using the radio buttons.
The beauty of using these two basic elements is that it works perfect without any css and Javascript. The css and Javascript is here to enhance the select list. But the core thing still works without these enhancement.
Why is this project awesome?
This project you will learn tobmake momentum list. Learn to customize the radio and checkbox. Learn to draw basic chart using the create js library. Also learn to use flex box to create the entire app layout.
The project is divided into the following steps.
- Building the app layout with flex
 - Listing the countries data
 - Basic list selection and calculation
 - Styling the radio and checkbox list
 - Drawing the chart
 - Adding the info panel and global app style
 
Preparing the project
Before we get started the project, let’s prepare several files. They are:
- Gulpfile.coffee
 - retinalize.coffee
 - shape.coffee
 
Gulpfile.coffee
 1 gulp.task 'js', ->
 2   gulp.src [
 3     './app/scripts/data.coffee'
 4     './app/scripts/retinalize.coffee'
 5     './app/scripts/shape.coffee'
 6     './app/scripts/chart.coffee'
 7     './app/scripts/view.coffee'
 8     './app/scripts/app.coffee'
 9   ]
10   .pipe coffee()
11   .pipe concat 'app.js'
12   .pipe gulp.dest './app/scripts/'
We make the retinalize class is ready.
 1 this.utility ?= {}
 2 
 3 this.utility.retinalize = (stage, updateCSS=true) ->
 4   canvas = stage.canvas
 5   utility.originalCanvasWidth = canvas.width
 6   utility.originalCanvasHeight = canvas.height
 7   
 8   return unless window.devicePixelRatio
 9 
10   ratio = window.devicePixelRatio      
11 
12   height = canvas.getAttribute('height')
13   width = canvas.getAttribute('width')
14 
15   canvas.setAttribute 'width', Math.round( width * ratio )
16   canvas.setAttribute 'height', Math.round( height * ratio )
17 
18   if updateCSS
19     canvas.style.width = width+"px"
20     canvas.style.height = height+"px"
21   
22   
23   stage.scaleX = stage.scaleY = ratio
Then we have another utility class that draws rectangle shape.
shape.coffee
 1 # alias
 2 cjs = createjs
 3 
 4 class this.DefaultShape extends cjs.Shape
 5   constructor: (@options={}) ->
 6     super()
 7     @initialize()
 8     @applyOptions()
 9   applyOptions: ->
10     @options.fillColor ?= null
11     @options.strokeColor ?= null
12     @options.strokeWidth ?= 1
13     @options.width ?= 100
14     @options.height ?= 100
15     @options.x ?= 0
16     @options.y ?= 0
17     return @options
18   
19 # Shapes
20 # options:
21 # width, height, fillColor, strokeColor, strokeWidth
22 class this.RectShape extends this.DefaultShape
23   constructor: (options={}) ->
24     super(options)        
25     @graphics
26       .setStrokeStyle @options.strokeWidth
27       .beginFill @options.fillColor
28       .beginStroke @options.strokeColor
29       .drawRect 0, 0, @options.width, @options.height
30     @x = @options.x
31     @y = @options.y
We’ll need the RectShape when we draw the chart in canvas.
There is basic CSS reset in the app.css file.
app.css
1 /* Basic reset */
2 html, body, p, ul, li, h1, h2, h3, div {
3   margin: 0;
4   padding: 0;
5 }
6 
7 * {
8   box-sizing: border-box;
9 }
Step 1 – Building the app layout with flex
In this task, we build the basic layout by using the flex box. We will create our own minimal flex layout styles. Every elements with class .container will treat as flex display. All their children would have flex:1 1 auto by default, unless .shrink class presents.
Time for Actions
Let’s follow the steps to create the app layout by using flex box.
- In the 
#appin HTML, we add the following elements.index.html
1<divid='app'class='container vertical'>2<divclass='charts container'>3<div><canvaswidth="300"height="150"></canvas></div>4<div><canvaswidth="300"height="150"></canvas></div>5</div>6<divclass='container'>7<divclass='list container vertical'>8<pclass='description'>Area:<spanclass='output1'>0</span>K km<sup>2</su\9p></p>10<ulid='countries-on-left'>11<li>ListItem</li>12<!--lotsoflisttimes-->13<li>ListItem</li>14</ul>15</div>16<divclass='list container vertical'>17<pclass='description'>Area:<spanclass='output2'>0</span>K km<sup>2</su\18p></p>19<ulid='countries-on-right'>20<li>ListItem</li>21<!--lotsoflisttimes-->22<li>ListItem</li>23</ul>24</div>25</div>26</div>
 - The minimal flex-based layout.
    
app.css
1/* Minimal flex grid */2.container{3display:flex;4}5.container.vertical{6flex-direction:column;7}8.container>*{9flex:11auto;10border:1pxsolidgreen;/* debug */11}12.container.shrink{13flex:01auto;14}
 - For the flex to work perfectly, we give a width and height to the container, which is the HTML and body element in this case.
    
app.css
1/* Global */23html,body{4width:100%;5height:100%;6}7#app{8width:100%;9height:100%;10background:IVORY;11}
 - The flex layout will change the dimension on the children elements. We can specific a minimal width and height so the flex layout will keep a minimal space for the elements.
    
app.css
1/* Canvas */2canvas{3max-width:100%;4min-height:150px;5}67.charts{8min-height:150px;9}1011/* Area Description */12p.description{13min-height:30px;14}
 - Finally, we make the long list 
overflow:scrolland enable the momentum scrolling.app.css
1/* List */2ul{3overflow-x:hidden;4overflow-y:scroll;5-webkit-overflow-scrolling:touch;6list-style:none;7padding:5px;8}
 
We created a minimal flex based layout.
For every .container class, we display the children as flex items.
The children inside the container has flex: 1 1 auto by default.
 1 .container                                                                    
 2 +----------------------------------------------------+                        
 3 | .container               .container                |                        
 4 | +----------------------+ +-----------------------+ |                        
 5 | |                      | |                       | |                        
 6 | |                      | |                       | |                        
 7 | |                      | |                       | |                        
 8 | |                      | |                       | |                        
 9 | |                      | |                       | |                        
10 | +----------------------+ +-----------------------+ |                        
11 |                                                    |                        
12 +----------------------------------------------------+                        
13 |                                                    |                        
14 |                                                    |                        
15 |                                                    |        .container {    
16 |                                                    |          display: flex;
17 |                                                    |        }               
18 |                                                    |                        
19 |                                                    |                        
20 |                                                    |                        
21 |                                                    |                        
22 |                                                    |                        
23 |                                                    |                        
24 |                                                    |                        
25 |                                                    |                        
26 |                                                    |                        
27 |                                                    |                        
28 |                                                    |                        
29 |                                                    |                        
30 |                                                    |                        
31 +----------------------------------------------------+                        
When the content exceeds the DOM container, we can use overflow scroll to make the content scroll inside the container. But this scroll wont have the momentum scrolling which common in the touch device. We need to add he the webkit scrolling to enable the momentum scrolling.
Step 2 – Listing the countries data
In this task, we obtain the list of countries areas and render them into the 2 lists we create in last step.
- In last task, we created a long list to test the layout. Now we don’t need that list anymore because we are rendering the list dynamically. Replace the 2 ul elements into the following.
    
index.html
1<ulid='countries-on-left'>2<liclass='template'><label><inputtype='radio'name='target-country'><spanc\3lass='name'>China</span></label></li>4</ul>5...6<ulid='countries-on-right'>7<liclass='template'><label><inputtype='checkbox'name='countries[]'><spanc\8lass='name'>China</span></label></li>9</ul>
 - The country data is obtained from Wikipedia. We put them into an array of object. For each object, we store the country name and the area.
    
data.coffee
1this.app?={}2this.app.data?={}34#Areaofcommoncountries5#Sourcefromhttp://simple.wikipedia.org/wiki/List_of_countries_by_area6this.app.data.areaOfCountries=[7{name:'China',area:9651.747}8{name:'Russia',area:17098.242}9{name:'Canada',area:9889.000}10{name:'USA',area:9826.675}11{name:'Australia',area:9596.691}12...
 - We have the data now, the next step is to render the data into the HTML list.
    
view.coffee
1this.app?={}23this.app.renderList=->4#List5template=$('#countries-on-left').find('.template')6countriesOnLeft=$('#countries-on-left')7forcountryinapp.data.areaOfCountries8clone=template.clone().removeClass('template')9clone.find('input[type="radio"]').val(country.area)10clone.find('.name').text(country.name)11countriesOnLeft.appendclone12#Removetemplateaftercloningdone.13template.remove()1415template=$('#countries-on-right').find('.template')16countriesOnLeft=$('#countries-on-right')17forcountryinapp.data.areaOfCountries18clone=template.clone().removeClass('template')19clone.find('input[type="checkbox"]').val(country.area)20clone.find('.name').text(country.name)21countriesOnLeft.appendclone22#Removetemplateaftercloningdone.23template.remove()
 - At last, we render the list in the 
app.coffee.app.coffee
1this.app?={}23this.app.renderList()
 
What just happened?
Some JavaScript tutorial may show you to render HTML directly inside the JavaScript.
I prefer using the template approach where the template element of the radio list item and checkbox list item are defined inside the HTML. When I need it, I clone the template and update the data inside.
In this example, we use the template at the initial stage and we don’t need it after the setup logic, so we can remove the clone after the list creation. In some projects, we may need the template at unknown time after the project is setup. In this case, we can hide all the template elements by using .template{display:none}.
Step 3 – Basic list selection and calculation
In this task, we handle the radio and checkboxes clicking and calculate the sum of area of selected countries.
Time for Action
Let’s follow the steps to handle the radio and checkbox selection.
- In the 
view.coffee, we add a function that check the input changes and display the calculation.view.coffee
1this.app.handleListChange=->2#ToggleChart13$('input[type="radio"]').change->4value=$('input[type="radio"]:checked').val()5$('.output1').text(Math.round(value))678#ToggleChart29$('input[type="checkbox"]').change->10sum=011$('input[type="checkbox"]:checked').each->12sum+=$(this).val()*113$('.output2').text(Math.round(sum))
 - In the 
app.coffeefile, we register the input changes handling by calling the function by the end of the logic.app.coffee
1this.app.handleListChange()
 
What just happened?
We handled the checkbox and radio selection.
The logic is based on the pseudo class :checked to get the HTML element of the checked input.
Step 4 – Styling the radio and checkbox list
In this project, we customize the radio and checkbox styles.
Time for Action
- All the customization are done is CSS. Add the following style in the 
app.cssfile.app.css
1/* Styling Radio and Checkbox */2input[type='radio'],3input[type='checkbox']{4display:none;5}67input[type='radio']+.name,8input[type='checkbox']+.name{9display:block;10font-size:1rem;11padding:1rem.5rem;12padding-left:2rem;13border:1pxsolidtransparent;14border-left:0;15border-right:0;16transition:all.3sease-out;17}1819input[type='radio']:checked+.name,20input[type='checkbox']:checked+.name{21border-color:DARKORANGE;22}2324/* Radio specific */25input[type='radio']+.name{26background:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/15649/radio.svg)\2710px50%no-repeat;28background-size:16px;29}30input[type='radio']:checked+.name{31background:SNOWurl(https://s3-us-west-2.amazonaws.com/s.cdpn.io/15649/radio\32-checked.svg)10px50%no-repeat;33background-size:16px;34}3536/* Checkbox specific */37input[type='checkbox']:checked+.name{38background:SNOWurl(https://s3-us-west-2.amazonaws.com/s.cdpn.io/15649/check\39ed.svg)10px50%no-repeat;40background-size:16px;41}
 
What just happened?
We customize the radio and checkbooks by hiding the default browser rendered radio and checkbooks button. Then we use the label to customize the button graphic
The label works because clicking in the label is identical to clicking on the input. That means when we click on the label, we are toggling the real radio boxes and check boxes.
So we can define the :checked style inn thr css where we rely to customize our graphics
We used the svg format for the checkbox and radio box graphics. We could use png. The reason we use svg is because the vector format scale better on the retina display and look sharper than using png format.
Step 5 – Drawing the chart
In this task, we draw the chart by using canvas.
Time for Action
- We add the 
#chart1-canvasand#chart2-canvasid to the canvas, so that our JavaScript logic can reference them.index.html
1<divclass='charts container'>2<div><canvasid="chart1-canvas"width="300"height="150"></canvas></div>3<div><canvasid="chart2-canvas"width="300"height="150"></canvas></div>4</div>
 - The chart logic is encapsulated into the 
chart.coffeefile. {title=”chart.coffee”} this.app ?= {}1#alias2cjs=createjs34classthis.app.Chart5#Entrypoint.6constructor:(canvasId)->7@stage=newcjs.Stage(canvasId)89utility.retinalize@stage,false10@canvasWidth=utility.originalCanvasWidth111213drawChart:(value)->14@stage.removeAllChildren()15#eachtile=10Kkm216areaForEachTile=10017tileWidth=tileHeight=1018margin=519numberOfTiles=value/areaForEachTile20cols=Math.floor((@canvasWidth-margin)/(tileWidth+margin))2122foriin[0...numberOfTiles]23x=margin+Math.floor(i%cols)*(tileWidth+margin)24y=margin+Math.floor(i/cols)*(tileHeight+margin)25shape=newRectShape26fillColor:'ORANGERED'27width:tileWidth28height:tileHeight29x:x30y:y31@stage.addChildshape3233#Drawoncanvas34@stage.update() - In the 
handleListChangefunction in view, we update the code to call the chart to draw the value.view.coffee
1this.app.handleListChange=(chart1,chart2)->2#ToggleChart13$('input[type="radio"]').change->4value=$('input[type="radio"]:checked').val()5$('.output1').text(Math.round(value))6chart1.drawChart(value)78#ToggleChart29$('input[type="checkbox"]').change->10sum=011$('input[type="checkbox"]:checked').each->12sum+=$(this).val()*113$('.output2').text(Math.round(sum))14chart2.drawChart(sum)
 - Finally, we create the chart instance and tell the view to draw the chart after any input changes.
    
app.coffee
1chart1=newapp.Chart("chart1-canvas")2chart2=newapp.Chart("chart2-canvas")34this.app.handleListChange(chart1,chart2)
 
 1 <----------------+  canvas_width +------------------------------------>         
 2                                                                                 
 3 +--------------------------------------------------------------------+          
 4 |                                                                    |          
 5 |  +--+  +--+  +--+  +--+  +--+  +--+  +--+  +--+  +--+  +--+  +--+  |          
 6 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |          
 7 |  +--+  +--+  +--+  +--+  +--+  +--+  +--+  +--+  +--+  +--+  +--+  |          
 8 |       ^                                                            |          
 9 |       |             ^ tile_width                                   |          
10 |       |                                                            |          
11 |       +                                                            |          
12 |      margin                                                        |          
13 |                                                                    |          
14 |                                                                    |          
15 |                                                                    |          
16 |                                                                    |          
17 |                                                                    |          
18 |   tiles_per_row = (canvas_width - margin) / (tile_width + margin)  |          
19 |                                                                    |          
20 |                                                                    |          
21 |   x = index % tiles_per_row                                        |          
22 |                                                                    |          
23 |   y = Math.floor(index / tiles_per_row)                            |          
24 |                                                                    |          
25 |                                                                    |          
26 |                                                                    |          
27 |                                                                    |          
28 +--------------------------------------------------------------------+          
Step 6 – Adding the info panel and global app style
In the last step, we add an information page and fine tune the global styles.
The info panel is presented by CSS 3d that rotate in from the left screen.
Time for Action
- We add an 
#info-btnthat trigger the info panel.index.html
1<divid="info-btn">2<ahref="#info"><span>Info</span></a>3</div>
 - The 
#info-panelcontains basic content.index.html
1<divid='info-panel'>2<h1>CountriesArea</h1>3<p>Thistoolletyoucomparetheareaofsomecountries.</p>4<p>Selectcountriesonbothlistandcomparethem.Eachtileinthechartis\5100Kkm<sup>2</sup></p>6<pclass='more-space'>Tapanywheretobegin.</p>7<p><small>Note:Weonlylistseveralcountriesinthisdemo.<br>Thesourcei\8sfrom<ahref="http://simple.wikipedia.org/wiki/List_of_countries_by_area">wiki\9pedia</a>.</small></p>10</div>
 - The info-btn sits on the top right corner.
    
app.css
1/* Info Button */2#info-btn{3position:absolute;4top:0;5right:0;6}7#info-btna{8width:44px;9height:44px;10display:block;1112background:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/15649/info.svg);\1314}15#info-btnaspan{16display:none;17}
 - The 
#info-panelis full screen that rotate into the view in 3D.app.css
1/* Info panel */2.hidden{3transform:rotateY(-90deg);4}5.show{6transform:rotateY(0);7}8#info-panel{9position:absolute;10top:0;11left:0;12width:100%;13height:100%;14background:ORANGERED;15color:white;1617transform-origin:00;18transition:all.3sease-out;1920display:flex;21flex-direction:column;22justify-content:center;23align-items:center;2425text-align:center;26}27#info-panela{28color:white;29}30#info-panelp{31margin:.5em;32}33p.more-space{34margin:2em;35}
 - For the rotate 3D effect, we add the 
perspectiveto body. We also fine tune the global style here.app.css
1body{2perspective:700px;3font-family:Verdana,sans-serif;4font-size:12px;56padding:5px;7background:ORANGERED;8}
 - Finally, make sure we have removed the debugging style.
    
app.css
1.container>*{2border:1pxsolidgreen;/* debug */3}
 
What just happened?
We have created a panel transition by using CSS 3D effects.
Summary
We learnt a lot in the chapter. We created a simple app that let user select countries and display their area in a tile bar chart. In conclusion, after reading this chapter, you should be able to:
- Build app layout by using CSS flex box.
 - Draw basic chart by using canvas and CreateJS library.
 
Project 4B – Drawing charts on canvas
In this project, we modify previous chapter to draw different types of chart by using the canvas tag and CreateJS library. We will also animate the chart by using the TweenJS, which is part of the CreateJS suite.
Mission Checklist
- Animated bar chart
 - Animated tiles
 - Pie chart
 - Animated pie chart
 
Preparation
In the chart.coffee, we have following alias declared for easier access to the CreateJS, TweenJS and the Ease library.
chart.coffee
1 # alias
2 cjs = createjs
3 Ease = cjs.Ease
4 Tween = cjs.Tween
Animated Bar Chart
There is not much difference in this project and previous project, in terms of app structure and logic flow. The main difference is in the chart.coffee where we draw different type of chart.
- We prepare the structure of 
Chartclass.chart.coffee
1this.app?={}23#alias4cjs=createjs5Ease=cjs.Ease67classthis.app.Chart8#Entrypoint.9constructor:(canvasId)->10@stage=newcjs.Stage(canvasId)11cjs.Ticker.setFPS(60);12cjs.Ticker.addEventListener'tick',@stage1314utility.retinalize@stage,false15@canvasWidth=utility.originalCanvasWidth16@canvasHeight=utility.originalCanvasHeight1718@initChart()1920initChart:->21@stage.removeAllChildren()2223#otherinitchartcodelater2425drawChart:(value,refValue=0)->26#drawthechartcodelater
 - When we init the chart, we create the rectangle shape and referencing line. They are put into 
this(@) scope for thedrawChartto access.chart.coffee
1initChart:->2@stage.removeAllChildren()34margin=556@shape=newRectShape7fillColor:'ORANGERED'8width:@canvasWidth-margin9height:110x:margin11y:@canvasHeight12@stage.addChild@shape1314@refLine=newRectShape15fillColor:'RED'16width:@canvasWidth-margin17height:118x:margin19y:@canvasHeight20@stage.addChild@refLine
 - We have drew the shape in the 
initChartmethod. ThedrawChartmethod is actually used to calculate the chart dimension based on the provided value. At last, we animate the shape to the target position and dimension by using TweenJS.chart.coffee
1drawChart:(value,refValue=0)->2areaForEachTile=1003scaleY=value/areaForEachTile4y=@canvasHeight-scaleY5cjs.Tween.get(@shape).to({scaleY,y},400,Ease.quartOut)67refY=@canvasHeight-(refValue/areaForEachTile)8cjs.Tween.get(@refLine).to({y:refY},400,Ease.quartOut)91011#Drawoncanvas12@stage.update()
 - We changed the 
drawChartmethod arguments. Now we need the reference value for positioning the reference line. So we update the view logic where it toggles thedrawChartmethod.view.coffee
1this.app.handleListChange=(chart1,chart2)->2#ToggleChart1and23$('input[type="radio"], input[type="checkbox"]').change->4#Chart15value=$('input[type="radio"]:checked').val()6$('.output1').text(Math.round(value))7chart1.drawChart(value)89#Chart210sum=011$('input[type="checkbox"]:checked').each->12sum+=$(this).val()*113$('.output2').text(Math.round(sum))14chart2.drawChart(sum,value)
 
What just happened?
We used the TweenJS the first time. Here is the syntax:
1 craetejs.Tween.get(anyObject).to({property:newValue, …}, duration, easeFunction)
Animated tiles chart
- Similar to the last example, we prepare the 
chart.coffeefile with the basic structure:constructor,initChartanddrawChart.chart.coffee
1classthis.app.Chart2#Entrypoint.3constructor:(canvasId)->4@stage=newcjs.Stage(canvasId)56cjs.Ticker.setFPS(60)7cjs.Ticker.addEventListener'tick',@stage89utility.retinalize@stage,false10@canvasWidth=utility.originalCanvasWidth11@canvasHeight=utility.originalCanvasHeight1213@initChart()14@lastNumberOfTiles=0151617initChart:->18@stage.removeAllChildren()1920#Initcodelater212223drawChart:(value)->2425#Codelater
 - The 
initChartmethod will draw all the tiles in the canvas area. By default all the tiles has 0 scaling so they are not visible at the beginning.chart.coffee
1initChart:->2@stage.removeAllChildren()34tileWidth=tileHeight=105margin=56leadingMargin=margin+tileWidth/278chartArea=(@canvasWidth-margin-leadingMargin)*(@canvasHeight-margin-leadi\9ngMargin)10tileArea=(tileWidth+margin)*(tileHeight+margin)11@maxNumberOfTiles=Math.floor(chartArea/tileArea)1213cols=Math.floor((@canvasWidth-margin)/(tileWidth+margin))1415@shapes=[]16foriin[0...@maxNumberOfTiles]17x=leadingMargin+Math.floor(i%cols)*(tileWidth+margin)18y=leadingMargin+Math.floor(i/cols)*(tileHeight+margin)19shape=newRectShape20fillColor:'ORANGERED'21width:tileWidth22height:tileHeight23x:x24y:y25@stage.addChildshape26@shapes.pushshape2728shape.regX=tileWidth/229shape.regY=tileHeight/230shape.scaleX=shape.scaleY=0
 - The 
drawChartmethod doesn’t draw the chart. It actually toggle thescaleXandscaleYof the existing tiles. It will scale up or down the tiles based on the given value.chart.coffee
1drawChart:(value)->2#eachtile=100Kkm23areaForEachTile=1004numberOfTiles=Math.floor(value/areaForEachTile)5foriin[0...@maxNumberOfTiles]6ifi<numberOfTiles7delay=(i-@lastNumberOfTiles)*58Tween.get(@shapes[i]).wait(delay).to({scaleX:1,scaleY:1},400,Ease.quar\9tOut)10else11delay=(i-@lastNumberOfTiles)*212Tween.get(@shapes[i]).wait(delay).to({scaleX:0,scaleY:0},400,Ease.quar\13tOut)14@lastNumberOfTiles=numberOfTiles
 - In the 
view.coffee, we ensure the logic to toggle both chart is correct as the following. It is the same as the project 4 code.view.coffee
1this.app.handleListChange=(chart1,chart2)->2#ToggleChart13$('input[type="radio"]').change->4value=$('input[type="radio"]:checked').val()5$('.output1').text(Math.round(value))6chart1.drawChart(value)78#ToggleChart29$('input[type="checkbox"]').change->10sum=011$('input[type="checkbox"]:checked').each->12sum+=$(this).val()*113$('.output2').text(Math.round(sum))14chart2.drawChart(sum)
 
What just happened?
We have created a nice animated tile-based bar chart.
Static pie chart
- There is only 1 pie chart. We change the HTML to contain 1 chart only.
    
index.html
1<divclass='charts'>2<canvasid="chart-canvas"width="150"height="150"></canvas>3</div>
 - We also center align the canvas.
    
app.css
1#chart-canvas{2display:block;3margin:auto;4}
 - The pie chart is different from the bar chart we have created.
    
chart.coffee
1classthis.app.Chart2#Entrypoint.3constructor:(canvasId)->4@stage=newcjs.Stage(canvasId)5cjs.Ticker.setFPS(60);6cjs.Ticker.addEventListener'tick',@stage78utility.retinalize@stage,false9@canvasWidth=utility.originalCanvasWidth10@canvasHeight=utility.originalCanvasHeight1112@initChart()1314initChart:->15@stage.removeAllChildren()161718drawChart:(leftValue,rightValue)->19x=@canvasWidth/220y=@canvasHeight/221r=502223globalRotation=-90*Math.PI/1802425percentage=rightValue/(leftValue+rightValue)26splitDegree=percentage*3602728#Arc129startAngle=0*Math.PI/180+globalRotation30endAngle=splitDegree*Math.PI/180+globalRotation3132shape=newcjs.Shape()33shape.graphics34.beginFill"GOLD"35.moveTo(x,y)36.arc(x,y,r,startAngle,endAngle)37.lineTo(x,y)3839@stage.addChildshape4041#Arc242startAngle=splitDegree*Math.PI/180+globalRotation43endAngle=360*Math.PI/180+globalRotation4445shape=newcjs.Shape()46shape.graphics47.beginFill"ORANGERED"48.moveTo(x,y)49.arc(x,y,r,startAngle,endAngle)50.lineTo(x,y)5152@stage.addChildshape
 - We changed to 1 chart, so we also change the 
handleListChangemethod to toggle the chart with both values form left and right list.view.coffee
1this.app.handleListChange=(chart)->2#ToggleChart3$('input[type="radio"], input[type="checkbox"]').change->4#Left5value=$('input[type="radio"]:checked').val()*16$('.output1').text(Math.round(value))78#Right9sum=010$('input[type="checkbox"]:checked').each->11sum+=$(this).val()*112$('.output2').text(Math.round(sum))1314#UpdateChart15chart.drawChart(value,sum)
 
Animated pie chart
- The animated chart.
    
chart.coffee
1classthis.app.Chart2#Entrypoint.3constructor:(canvasId)->4@stage=newcjs.Stage(canvasId)5cjs.Ticker.setFPS(60);6cjs.Ticker.addEventListener'tick',@stage78utility.retinalize@stage9@canvasWidth=utility.originalCanvasWidth10@canvasHeight=utility.originalCanvasHeight1112@initChart()1314initChart:->15@pieData={16splitDegree:017}1819updateChart:(e)=>20#Codetodrawarcshapelater2122drawChart:(leftValue,rightValue)->23#Codetostarttheshapedrawinglater
 - In the 
updateChartmethod, we clear the stage and draw the arc again based on the currentsplitDegree.chart.coffee
1updateChart:(e)=>2x=@canvasWidth/23y=@canvasHeight/24r=505globalRotation=-90*Math.PI/18067@stage.removeAllChildren()89#Arc110startAngle=0*Math.PI/180+globalRotation11endAngle=@pieData.splitDegree*Math.PI/180+globalRotation1213shape=newcjs.Shape()14shape.graphics15.beginFill"GOLD"16.moveTo(x,y)17.arc(x,y,r,startAngle,endAngle)18.lineTo(x,y)1920@stage.addChildshape2122#Arc223startAngle=@pieData.splitDegree*Math.PI/180+globalRotation24endAngle=360*Math.PI/180+globalRotation2526shape=newcjs.Shape()27shape.graphics28.beginFill"ORANGERED"29.moveTo(x,y)30.arc(x,y,r,startAngle,endAngle)31.lineTo(x,y)3233@stage.addChildshape
 - The 
splitDegreevalue is actually changing to create the animated effect.chart.coffee
1drawChart:(leftValue,rightValue)->2percentage=rightValue/(leftValue+rightValue)3splitDegree=percentage*36045Tween.get(@pieData).to({splitDegree},400,Ease.quantOut).addEventListener('c\6hange',@updateChart)
 
What just happened?
We used the TweenJS to animate the pieData object. TweenJS is an independent library that the target is not necessary to be any CreateJS display object. We can provide any object and ask the TweenJS to tween any numeric property. In the change event, we know the changes happened so we can updated our canvas in our own way. In this example, we create a new pie chart based on the changing value.
Summary
Drawing chart is one of the common canvas usage.
Further challenges
We have discussed the usage of gyroscope sensor in the chapter Rain or Not. What if combine what I have learned there with the chart drawing together? Try creating an inspector as following that shows the value history of the sensor.

Rotation Inspector
You can test the real application by using the following links with your devices.
- Gyroscope Rotation: 
http://mztests.herokuapp.com/rotation/ - Accelerometer: 
http://mztests.herokuapp.com/motion 
Optionally, you may download the app in the Play Store.
https://play.google.com/store/apps/details?id=net.makzan.gyroinspcetor&hl=en
The rotation value ranged from -365 to +365. When we use the following chart, the rotation value shows as a history for better inspection.

Chart explanation
Change Log
Version: 2015-01-09
The schedule of the book is a weekly release for new chapters. Then it will get edited and done in early 2015. The following is the change log of this book.
Todos
- Add chapter 1b explanation.
 - Add chapter 2 explanation.
 - Split chapter 3 into steps.
 
What have done
- 2015-01-09
    
- Added project 4B summary.
 
 - 2015-01-03
    
- Added chapter 4B code example, which draws several different charts based on the same data in chapter 4.
 
 - 2014-12-27
    
- Added chapter 4 code example, which draws a interactive chart based on the user selection.
 
 - 2014-12-13
    
- Added chapter 3 code example, which demonstrated a solar system app with parallax effects.
 - Updated explanation in previous chapters.
 
 - 2014-12-06
    
- Added chapter 2 code example, which separates code into modules by the modal-view-controller pattern.
 
 - 2014-11-22
    
- Added chapter 1b example, which makes use of the Canvas transition in DOM-based application.
 
 - 2014-11-07
    
- Improved chapter 1 explanation.
 
 - 2014-10-26
    
- Added CreateJS introduction.
 - Organized the book content with front matters and 2 parts of content.
 - Added chapter 1 – basic app with animated transitions
 
 - 2014-10-19
    
- Added changes log to the book.
 
 - 2014-10-18
    
- Initial the book with pre-request section.
 
 
Thanks again for reading the book. Hope you enjoy the example porjects and learn to build mobile app with the CreateJS library.
If you find any questions, you’re welcome to contact me for any questions. You can find me at:
- Website: makzan.net
 - Email: mak@makzan.net
 - Twitter: @makzan