2013-03-30

Canvas Balloons

Canvas

Basic usage of the HTML canvas element is covered in the Canvas Crossroad post.


Picking colors

Balloons in the night look pretty awesome, although it's generally not advisable to fly them at night. The Balloons are displayed in various random colors, but the basic color has to be dark. The RGB color code describes the intensity of each color component. In order to keep the colors dark, one has to limit the amount of each color intensity to a rather low level. (in this case up to 50 from possible 255).

 function getBalloonColor() {

  var r = randomInt(50).toString(16);
  var g = randomInt(50).toString(16);
  var b = randomInt(50).toString(16);

  r = r.length < 2 ? '0' + r : r;
  g = g.length < 2 ? '0' + g : g;
  b = b.length < 2 ? '0' + b : b;

  return '#' + r + g + b;
 }

While the balloon changes climbing state it's color has to be darken or lighten by some percentage to simulate the flame. It's done by this function:

 function shadeColor(color, percent) {

  var num = parseInt(color.slice(1),16),
   amt = Math.round(2.55 * percent),
   R = (num >> 16) + amt,
   B = (num >> 8 & 0x00FF) + amt,
   G = (num & 0x0000FF) + amt;

   return "#" + (0x1000000 +
    (R < 255 ? R < 1 ? 0 : R : 255) * 0x10000 +
    (B < 255 ? B < 1 ? 0 : B : 255) * 0x100 +
    (G < 255 ? G < 1 ? 0 : G : 255)).toString(16).slice(1);
 }

The color of a climbing balloon should be darker further away from the flame. So a gradient is used. The HTML canvas gradient works only within a predefined range on a canvas. The example bellow shows a linear gradient from the top to the bottom of a balloon. Notice that the horizontal component is set to 0 (this is a vertical linear gradient):

 var linGrad = ctx.createLinearGradient(0, this.y, 0, this.y + this.height);

 linGrad.addColorStop(0, ballonColor);
 linGrad.addColorStop(1, shadeColor(ballonColor, ballonFlameLighter));

 ballonColor = linGrad;


Balloon shape

The balloon shape is drawn with

  • half circle
  • triangle
  • flame - small circle
  • basket - small rectangle
Since the balloons are moving, we have to repaint the sprite. I've simply used the same function for drawing with canvas background color instead of the balloon parts colors. If a clearRect method had been used the transition of a balloon over a star would look unnatural.
  // top balloon circle
  ctx.beginPath();
  ctx.arc(this.x + this.width / 2, this.y + this.radius,
    this.radius, Math.PI, 0, false);
  ctx.fill();

  // triangle
  ctx.beginPath();
  ctx.moveTo(this.x, this.y + this.radius - 1);
  ctx.lineTo(this.x + this.width / 2, this.y + this.height - this.boxHeight);
  ctx.lineTo(this.x + this.width / 2 + 1, this.y + this.height - this.boxHeight);
  ctx.lineTo(this.x + this.width, this.y + this.radius - 1);
  ctx.lineTo(this.x, this.y + this.radius - 1);
  ctx.fill();

  // flame
  if (this.climbing) {
   ctx.fillStyle = canvasBackground;
   ctx.beginPath();
   ctx.arc(this.x + this.width / 2, this.y + this.height - this.boxHeight,
     flameSize, 0, 2 * Math.PI, true);
   ctx.fill();
  }

  // basket
  ctx.fillStyle = gondolaColor;
  ctx.fillRect(
   this.x + this.width / 4 + this.width / 8,
   this.y + this.height - this.boxHeight,
   this.width / 4,
   this.boxHeight
  );


Collision detection

The collision detection between two balloons is bounding rectangles based. Before every balloon movement there is a collision check to the current positions of the other balloons. To detect collision between two rectangles I've used the following function:

 function checkCollision(obj1, obj2) {
  return !(
    (obj1.y + obj1.height < obj2.y) ||
    (obj1.y > obj2.y + obj2.height) ||
    (obj1.x > obj2.x + obj2.width) ||
    (obj1.x + obj1.width < obj2.x)
  );
 }


Detecting keyboard keyes

In order to make the simulation interactive the parameters can be controlled with the keyboard keys. The key detection in JavaScript is pretty straight forward:

 document.onkeydown = function(e) {
  if(e.keyCode == 37) {
   // left
  } else if(e.keyCode == 39) {
   // right
  }  else if(e.keyCode == 38) {
   // up
  } else if(e.keyCode == 40) {
   // down
  } else if (e.keyCode == 13) {
   // enter key
  }

  return false;
 };


And here's my example on:
Codepen
GitHub

2013-03-20

Canvas Crossroad

Canvas context

The canvas element is part of HTML5 and allows for dynamic, scriptable rendering of shapes and images. The actual drawing happens within a context. In this example we'll use 2d context for drawing:

 var canvas = document.createElement('canvas');

 canvas.width = window.innerWidth;
 canvas.height = window.innerHeight;

 var ctx = canvas.getContext('2d');
 document.body.appendChild(canvas);


Drawing shapes and lines

In order to make the changes to the context as easy as possible the best practice is to save the context before doing any changes and restoring it after we are done with drawing. Especially if we change context rotation, colors and fill styles:

  ctx.save();
  ...
  ctx.restore();

The crossroad canvas example uses these constructs for drawing shapes:

  • arc
  • fillRect
  • moveTo + lineTo


Circle

There's no method named circle, instead we use arc and make one from 0 to 2 * Pi:

 ctx.fillStyle = 'red';
 ctx.beginPath();

 ctx.arc(x, y, radius, startAngle, endAngle, antiClockwise);

 // ctx.stroke() would draw circumference, fill makes a circle
 ctx.fill();


Rectangle

Drawing rectangles is pretty straight forward. One must set coordinates, width and height:

 ctx.fillRect (x, y, width, height);


Lines

When working with lines we can move starting point and then make a line to some other position:

 ctx.beginPath();

 ctx.moveTo(x1, y1);
 ctx.lineTo(x2, y2);

 ctx.closePath();
 
 ctx.stroke();


Clearing the drawing

Very important when doing animations because calculating what pixels to clear every time would be pretty complicated to code. Changing a small piece of canvas takes less time than clearing everything and then drawing it again. In the past developers kept an off screen canvas and did drawing on it and then at the end of the animation loop they copied the off screen canvas to the on screen canvas.

Most of the browser vendors started to do this automatically so in modern browsers keeping an off screen canvas actually degrades performance.

 ctx.clearRect (x, y, width, height);


Collision detection

All objects in the crossroad animation are represented as circles. The formula for collision detection between two circles is:

 function collision(c1, c2) {
 
  var dx = c1.x - c2.x;
  var dy = c1.y - c2.y;
  var dist = c1.radius + c2.radius;
   
  return (dx * dx + dy * dy <= dist * dist);
 }
If the movement happens along a single axis line it's even simpler we just need to check the coordinate on the relevant axis (in this case x):
  Math.abs(x1 - x2) > (radius + safety_distance)


requestAnimationFrame

The browser vendors optimized the usual timer animation loop and provided api for it. But since some vendors don't support it or the users have older browsers we use this pattern:

 // shim layer with setTimeout fallback
 window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame    ||
    function( callback ){
     window.setTimeout(callback, 1000 / 60);
    };
 })();
 
 (function animloop(){
  requestAnimFrame(animloop);
  // call drawing functions here
 })();


requestAnimationFrame

And here's my example on:
CSSDeck
Codepen
GitHub

2013-03-09

Git

Introduction

Git is a distributed source code management system working with a complete repository copied and detached from some central server.

Git system stores complete file snapshots in it's database whereas traditional systems store initial file and differences made to them over time. Once the file is in Git's database, it stays there forever.

Git operates on top of a simple database containing project files as binaries. Every binary in this database is represented with a key.

Key is calculated by running SHA-1 hash function on file's content and that results in a 40 characters unique string key. Most of the time first 7 characters are enough to identify something in the project.

Every snapshot contains information about:

  • what binary objects are included (id's from internal database)
  • info about previous snapshot (pointer)
  • data describing the change

Global setting

Enable the Git to fill the data about the author of the change by providing this basic info:

   git config --global user.name "Your Name"
   git config --global user.email "email@example.com"

Creating a repository

  • new local repository (creates a folder with .git folder inside it):
         git init project_name
  • clone a repository from a remote system:
         git clone http://example.com/project.git

Basic workflow:

  1. edit files
  2. define files going in the next snapshot with specific file name or simply by "."
         git add .
  3. review changes
         git status
         git diff
  4. commit (make snapshot)
         git commit -m "message describing new snapshot"
Git doesn't track empty folders, if needed, users usually put empty .gitkeep file into it. If some files don't need to be under version control there is a special file .gitignore with a list of files and directories not under version control. One can use special character " * " and "." in this file.

Branches

Branches are lightweight pointers to snapshots (commits), to list branches use:

   git branch
Initially every project has one branch called master (created in the init). Nothing special about the name master, it's default. To make a new branch and start working in it use:
   git checkout -b newbranch master

If you want to switch to existing branch use:

    git checkout existingbranch

Merging

To merge something into a branch that you are currently working on use:

   git merge coolbranch
If there are same files changed in both branches, Git will inform us about a merge conflict, then we have to resolve the conflict manually and commit the changes. This results with a new snapshot and the branch pointer moves to this new snapshot.

Collaboration

In order to do the collaboration, we need location names of remote repositories. Usually we make a short alias for them. The most common alias is origin refering to some "central" repository, but we can add other physical repositories for instance by team member name.

   git remote add tom git://...project.git

Branches in other repositories are referred to as tom/master, origin/master... To get the remote snapshots and remote branching info we use fetch command.

Fetch doesn't change our branch pointers or changes our existing snapshot data, it only adds the information to the database from the remote system. This is why Git is called distributed. To get tom's Git database and merge our current branch to his superbranch we use:

   git fetch tom
   git merge tom/superbranch

Remote branch pointers are moved with push command. But, if the branch pointer on the server points to a newer snapshot than our local one, we'll get an error. Then we have to fetch the remote database, do the merge and then tell to remote to point the branch to a new snapshot:

   git push origin master

Log

To get the history of snapshots use the log command. The oneline option prints just the basic information, and the graph option shows snapshot connections.

   git log --oneline --graph

The log command above shows only the snapshots leading up to the current branch pointer. Some snapshots from other branches might not be visible.

If we would like to see what's in the master and not in the coolbranch we would use:

   git log master ^coolbranch

http://git-scm.com/


@msvaljek

2013-03-07

Modernizr

Introduction

Modernizr is a small JavaScript library for detecting browser capabilities.

  • creates an object Modernizer with boolean properties about available features
  • provides a script loader for pulling scripts to backfill functionality (polyfills)

Keep in mind that polyfills sometimes decrease user expirience because of poor performance.

Modernizr Download is usually customized and contains only specific checks.

It's a best practice to include the Modernizr script before the end of a head tag to avoid browser (IE) compatibility issues.

Modernizr.load function

Loads a script asynchronously based on a test result, it's not a default Modernizr feature and has to be checked for in the download. Load function usage example:

   Modernizr.load({
    test: Modernizr.geolocation,
    yep : 'geo.js',
    nope: 'geo-polyfill.js'
   });

Usually Modernizr loads more than one script, an has more than one test.

   Modernizr.load([
    {
     // run a test, include script when capability missing
     test : Modernizr.fontface && Modernizr.canvas && Modernizr.cssgradients,
     nope : ['presentational-polyfill.js', 'presentational.css']
    },
    {
        test : Modernizr.websockets && window.JSON,
        nope : 'functional-polyfills.js',
        // scripts under both are always loaded
        both : [ 'app.js', 'extra.js' ],
     complete : function () {
         // this function runs when all of the previous scripts are loaded
         // and when all scripts from this secion are loaded.
         myApp.init();
        }
    },
    // this script is included without checking any conditions
    'post-analytics.js'
   ]);

CSS for the unsupported features

CSS markup can be adapted to the availability of features. For instance, if css gradients are not supported, perhaps we want to include background image instead. But be careful, browser usually preloads images, so this could cause unwanted http requests. Note that the missing class has a no-missing_feature name:

   .no-cssgradients {
    background: url("images/glossybutton.png");
   }

Detecting css prefixes from javascript

When using css animations and transitions in javascript, it's useful to know how the browser prefixes certain css features.

   Modernizr.prefixed('boxSizing')

http://modernizr.com/docs/


@msvaljek

2013-03-05

compass

Introduction

Compass is a CSS Authoring Framework with a lot of reusable patterns. Developers spend less time on browser prefixes, css2 opacity, browser checking, IE hacks, reset stylesheets and so on...

Installation:

   gem install compass
Create new project (this command creates a folder and a substructure):
   compass create myproject
After creating project tell the compass to watch the project for changes:
   compass watch myproject

Pattern usage

In order to find out available constructs in the framework use compass documentation

Before using any compass patterns, we have to declare them:

   @import "compass/css3/border-radius";
Declaring a higher level group gives acces to all child patterns (same as above):
   @import "compass/css3";
Actually using a declared border-radius import:
   .box {
    @include border-radius(5px);
   }
The stylesheets get compiled into the stylesheets folder, the compiled css files contain references to scss file lines.
   /* line 8, ../sass/screen.scss */
   #elem {
     -webkit-box-shadow: red, 2px, 5px, 10px;
     -moz-box-shadow: red, 2px, 5px, 10px;
     box-shadow: red, 2px, 5px, 10px;
   }

Best practice

Best practice is to make base.scss with includes, global variables and pattern variable overrides:

   @import "compass/reset";
   @import "compass/css3";
   @import "compass/utilities";

   $default-box-shadow-color: red;

Import the base.scss in the custom scss files:

   @import 'base';

   .box {
    @include border-radius(5px);
   }

   #myelemid {
    @include box-shadow(blue, 1px, 3px, 5px);
   }

compass and 960

Installation:

   gem install compass-960-plugin
Create new project:
   compass create -r ninesixty my_project960 --using 960


http://compass-style.org/


@msvaljek

2013-03-04

Sass

Intro

Sass is an extension of CSS3 with nested rules, variables, mixins, selector inheritance and more.

Installation:

   gem install sass

Edit .scss files and they get "compiled" into output css files. To do this use:

   sass --watch style.scss:style.css

Before going into production, it's a good practice to compress the output css files:

   sass --style compressed --watch style.scss:style.css

Variables

Variables are declared with $variable_name:

   $my-color: blue;
   body {
    background: $my-color;
   }

Nested rules

Nicer style organization (ie. avoid repeating ul li selector):

   ul {
       background: red;
       li {
           display: inline;
       } 
   }

Mixins

Mixins behave like functions, use them when applying block of information to multiple properties like repeating vendor prefixes for rounded corners.

Declare a mixin with @mixin, specify the variables in the Parentheses, assign them a default value with ":". Call the mixin with @include mixin_name:

   @mixin rounded ($radius : 5px) {
       -webkit-border-radius: $radius;
       border-radius: $radius;
   }

   #mybox {
       @include rounded(10px);
   }

Selector inheritance

It's pretty much like classes in the OO programming, uses @extend keywoard:

   .myrounded {
       -webkit-border-radius: 5px;
       border-radius: 5px;
   }

   #box3 {
       @extend .myrounded;
       color: yellow;
   }


http://sass-lang.com/


@msvaljek