Friday, April 8, 2016

HTML5 Graphics

About JavaScript and HTML5

JavaScript logoHTML5 is composed of new elements, but it also comes with many JavaScript APIs for controlling video and sound, drawing and animating things in the new <canvas> element, for offline applications, persistence, geolocation, orientation, etc.
So yes, during this course, in particular during Week 3 and 4, you will have to do a bit of JavaScript. But, DON'T PANIC!
We are providing here a basic guidance on JavaScript. If you want to learn more, there are lots of resources available on the Web. This document is here to help you start in good conditions. Remember that one great thing with these MOOCs courses is that everybody can help each other. Some of the classmates are really good in JavaScript and are usually very happy to help others when they encounter difficulties.
You will learn a lot by looking at examples, tweak them, clone and modify them, etc. Many students in the past who were real JavaScript beginners managed to do all the assignments (drawing and animating a monster with keyboard/mouse interaction)! And they did by just studying the provided examples.

EXTERNAL RESOURCES

  • The book I used to learn JavaScript myself: Object Oriented JavaScript by Stoyan Stefanov
  • Mozilla Developper Network has a JS guide too.
Extracts from the forum posts:

WHAT DO YOU NEED? HOW TO DEBUG? HOW TO CATCH ERRORS?

We will not look at the JavaScript syntax here, but more at "JavaScript in the browser", how it works, how to start writing code, etc.
First of all, you need to find a way to debug your code and see errors. If your work does not produce any results, you must know why!
For that you will use the dev. tools of your browser. Press F12 on Windows or cmd-alt-i on Mac to open the dev. tools, then go to the console tab: this is where errors will be displayed, or messages of your own (use the console.log(string) JavaScript function in the JavaScript code embedded in your html page). In the console, you will be able to type any JavaScript command.
Let's look at  this example on JS Bin:
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html>
  <head>
  <meta charset=utf-8 />
  <title>Web Audio API</title>
  <script>
   console.log("Some JavaScript code has been executed");
  </script>
  </head>
  <body>
    <h1>JavaScript debugging using the dev tool console</h1>
  </body>
</html>
The most simple way to add JavaScript code in an HTML page, is by using the <script>...</script>element.
The code in this example is executed sequentially when the page is loaded: the JavaScript code is executed before the browser could see the rest of the page (as the <script></script> is located before the <body>).
The H1 element, for example, does not exist in the Document Object Model, and has not yet been displayed when the JavaScript code is executed. If we move the <script></script> at the end of the document, then the H1 would have been built before the JavaScript code is executed.
The only line of code we have is console.log("Some JavaScript code has been executed");
This means "display in the JavaScript console the message...". If we open the console tab provided by jsbin.com in a dedicated tab (that redirects all console.log() messages), and re-execute the page (just type a space at the end of a line, this will re-render the page and display the message in the console), we see the message in the console tab, as well as in the dev. tools console. This is illustrated by the image below:
java script console view 1
It is also possible to use the "real dev. tool console", and for this I recommend to run the application in a single window, not in the JS Bin editor. Press the black arrow on the top right of the output window, this will render the page as a standalone Web page, then press F12. You should see:
view of the javascript console
Ok, now, let's make an error, change console.log() into consollle.log(). Let's see what happens:
view of the javascript console
And if we run it standalone and use the dev. tool console:
View of the JavaScript console
And if we click on the line number in the right, the dev. tool shows the source code centered on the line that caused the error:
View of the JavaScript console
Without such tools, debugging JavaScript code is impossible. So you need to look at some basic tutorials on how to use the dev. tools of your browsers, as the way they work differ one from another. But the principles remain the same.

Draw and animate graphics: the <canvas> element

INTRODUCTION

canvas element logoThe <canvas> tag is one of the "Flash killer" features of HTML5. This course will focus on the fundamental drawing capabilities of the HTML5 canvas.
The W3C HTML5 specification about the <canvas> element states that "The canvas element provides scripts with a resolution-dependent bitmap canvas, which can be used for rendering graphs, game graphics, or other visual images on the fly."
The canvas has been designed for pixel-based graphics, while SVG (Scalable Vector Graphics, another W3C standard) is for vector-based graphics.
Indeed, the canvas JavaScript drawing API supports different kind of shapes: lines, rectangles, ellipses, arcs, curves, text, images. Some drawing styles need to be specified that will affect the way shapes are drawn (color, drawing width, shadows, etc.). An alpha channel for drawing in transparent mode is also supported, as well as many advanced drawing modes and global filters (blur, etc.).
The canvas is also used to do animations at 60 frames per second (useful for games), to display videos with special effects, to display a webcam stream, and so on.

EXAMPLES

Example 1

Foot Chinko, one of the most popular free HTML5 games (released in 2015):
foot chinko one of the best html5 2D game of 2015

Example 2

html5 data visualization

Example 3

html5 60 frames/s animation
Performance is generally good today, since most Web browsers support hardware acceleration or will add support soon. Back in 2009, this demo ran at only a few images per second on some low-end computers (smartphones that could run HTML5 demos, such as this one, did not exist at that time ) due to the lack of hardware acceleration support in the browser's implementations of the canvas API.
Note: 3D drawing using the WebGL API is also possible in a <canvas>, but will not be covered in this course. For the most curious among, please have a look at the two popular libraries for doing 3D drawing/animation in a <canvas>BabylonJS and ThreeJS.

CURRENT SUPPORT BY BROWSERS (AS OF 2015)

canvas support
An up-to-date version of this table is at: http://caniuse.com/#feat=canvas 

GOOD EXTERNAL RESOURCES

How to make the HTML5 canvas accessible to users with disabilities?

INTRODUCTION

The dynamic nature of the <canvas> element has made it difficult to use in applications that need to be accessible to people with disabilities. To be accessible, it must meet the following principles:
  • Providing alternative content for what is drawn on the <canvas>,
  • Exposing the location of shapes, paths, images drawn on the <canvas> to assistive technologies,
  • Visually indicating whether or not a shape in the canvas had keyboard focus.

THE W3C CANVAS TASK FORCE

The Canvas Task Force of the W3C's HTML Working Group is working on different features to be added to the HTML5.1 canvas specification in order to address canvas accessibility. This is only preliminary work and browsers implementations are not available yet.

Read more on this topic: 

Canvas cheatsheet with all API methods and properties

This is a valuable resource we recommend either printing or keeping open in a separate browser tab. The original version was located at "http://blog.nihilogic.dk/2009/02/html5-canvas-cheat-sheet.html", but this URL does not work anymore. Here, we share here the mirrored versions (HTML and PDF ones).

HTML VERSION

PDF VERSION

The image links to a PDF, just click on it. 
canvas sheet API

The canvas coordinate system

X AXIS IS HORIZONTAL, DIRECTED TO THE RIGHT
Y AXIS IS VERTICAL, DIRECTED DOWNWARDS

The coordinate system used for drawing in canvases is similar to the one used by many drawing APIs like Java2D: the (0 , 0) is in the top left corner while the X axis is going to the right and the Y axis to the bottom, like in the following picture (author Mark Pilgrim):
coordinate system


Typical use of the <canvas> element

DETAILED EXPLANATION OF THE EXAMPLE SHOWN IN THE VIDEO

Here are the different steps, a little more detailed, of the example demonstrated in the above video:

1 - Add the <canvas> element into an HTML page

  1. <canvas id="myCanvas" width="300" height="225">
  2.     Fallback content that will be displayed in case the web browser
  3.     does not support the canvas tag or in case scripting
  4.     is disabled.
  5. </canvas>
Place code similar to the above somewhere in an HTML page. This example defines an area of 300 by 225 pixels on which content can be rendered with JavaScript.
Normally you should see nothing as a result, by default canvases are "transparent".
Make it visible using CSS: A good practice when you learn using canvases is to use some CSS to visualize the shape of the canvas. This is not mandatory, just a good trick...
The three lines of CSS will create a border around the canvas with id="myCanvas", of 1 pixel width, in black:
CSS code:
  1. <style>
  2.     #myCanvas {
  3.         border:1px solid black;
  4.     }
  5. </style>

2 - Select the <canvas> element for use from JavaScript

We can have more than one <canvas> in a single page, and canvases will be manipulated with JavaScript like other elements in the DOM.
For example with:
  1. var canvas = document.getElementById("myCanvas");
... or with the querySelector() method introduced by HTML5, that use the CSS selector syntax for selecting elements:
  1. var canvas = document.querySelector("#myCanvas");

3 - get a "2D context" associated with the canvas, useful for drawing and setting drawing properties (color, etc.)

Once we have a pointer to the <canvas>, we can get a "context".
This particular object is the core of the canvas JavaScript API.
It provides methods for drawing, like fillRect(x, y, width, height) for example, that draws a filled rectangle, and properties for setting the color, shadows, gradients, etc.
Getting the context (do this only once):
  1. var ctx=canvas.getContext('2d');
Set the color for drawing filled shapes:
  1. ctx.fillStyle='red';
Draw a filled rectangle:
  1. ctx.fillRect(0,0,80,100);

COMPLETE EXAMPLE THAT DRAWS A FILLED RECTANGLE IN RED

Result:

red rectangle draw in a canvas

Source code:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.  <style>
  5.      #myCanvas {
  6.          border: 1px solid black;
  7.      }
  8.  </style>
  9. <script>
  10.    var canvas, ctx;
  11.  
  12.    function init() {
  13.      // This function is called after the page is loaded
  14.      // 1 - Get the canvas
  15.      canvas = document.getElementById('myCanvas');
  16.      // 2 - Get the context
  17.      ctx=canvas.getContext('2d');
  18.      // 3 - we can draw
  19.      drawSomething();
  20.    }
  21.    function drawSomething() {
  22.      // draw a red rectangle
  23.      ctx.fillStyle='#FF0000';
  24.      ctx.fillRect(0,0,80,100);
  25.    }
  26. </script>
  27. </head>
  28. <body onload="init();">
  29.     <canvas id="myCanvas" width="200" height="200">
  30.             Your browser does not support the canvas tag.
  31.     </canvas>
  32. </body>
  33. </html>

Explanation

Only access elements when the DOM is ready:
Notice that we wrote an "init" function (line 12) that is called only when the page has been entirely loaded (we say "when the DOM is ready"). There are several ways to do that. In this example we used the <body onload="init();"> method, at line 32.
It's a good practice to have such a function, as we cannot access the elements of the page before the page has been loaded entirely and before the DOM is ready.
Another way consists in putting the JavaScript code at the end of the document (between <script>...</script>), right before the </body>. In that case when the JavaScript code is executed, the DOM has already been constructed.
Start by getting the canvas and the context:
Before drawing or doing anything interesting with the canvas, we must first get its drawing "context". The drawing context defines the drawing methods and properties we can use.
Good practice is to get the canvas, the context, the width and height of the canvas and other global objects in this "init" function.
After the context is set, we can draw, but before, let's set the current color for filled shapes:
The example shows at line 27 the use of the fillStyle property - useful for specifying the way shapes will be filled. In our case this line indicates the color of all the filled shapes we are going to draw:
  1. ctx.fillStyle='#FF0000';
The context property named fillStyle is used here. This property can be set with a color, a gradient, or a pattern. We will see examples of these later on in the course.
When we set it with a color, we use the CSS3 syntax.
The example says that all filled shapes will use the color "#FF0000", which corresponds to a pure red color using the CSS RGB hexadecimal encoding (we could also have used ctx.fillStyle='red');
Then we can draw:
  1. ctx.fillRect(0,0,80,100);
This line is a call to the method fillRect(top left X coordinate, top left Y coordinate, width, height), which draws a filled rectangle.
The way the rectangle will be filled depends on the current value of several properties of the context, in particular the value of the fillStyle property. So, in our case, the rectangle will be red.

SUMMARY OF THE DIFFERENT STEPS

    1. Declare the canvas, not forgetting to add an id attribute, and fallback content:  
      <canvas id="myCanvas" width="200" height="200">
      ...fallback content...
      </canvas>
    2.  Get a reference to the canvas in a JavaScript variable using the DOM API: 
      var canvas=document.getElementById('myCanvas');
    3. Get the context for drawing in that canvas:  
      var ctx=canvas.getContext('2d');
    4. Specify some drawing properties (optional):  
      ctx.fillStyle='#FF0000';
    5. Draw some shapes
      ctx.fillRect(0,0,80,100);
  1. Drawing principles

    MORE DETAILS ABOUT THE "CONTEXT" OBJECT

    Before we go on, we should take some time to clarify the way we draw on HTML5 canvases. We already mentioned that we use a graphic context for all the main operations. Whenever a shape, a text, or an image is drawn, the current values of the different properties of the graphic context are taken into account. Some are relevant only for certain kinds of shapes or drawing modes, but you must be aware that it is always the current values of these drawing properties that are used.
    We'll see later on that there are ways to save and restore this whole set of values, but for now, let's go into more detail on some of the properties and methods we've already encountered, and introduce new ones.

    MORE ABOUT PROPERTIES AND METHODS OF THE CONTEXT OBJECT

    fillStyle is a property of the context, similar in a way to a CSS property.

    Its value can be one of the following:
      • a color,
      • a pattern (texture), or
      • a gradient.
    The default value is the color black. Any kind of drawing in "fill mode" will use the value of this property to determine how to render the "filled part" of the drawing: any filled rectangle will be filled black by default, any filled circle will be filled in black, and so on.
    As long as we don't modify the value of this property, all drawing commands for filled shapes will use the current value.
    Note that we will study in detail how to use colors, gradients and patterns later, but for now we introduce some properties and values so that you can understand the principles of canvas drawing.
    fillStyle and the other context properties can be considered to be "global variables" of the context.

    fillRect(x, y, width, height):  a call to this method draws a filled rectangle.

    The two first parameters are the coordinates of the top left corner of the rectangle. This method uses the current value of the filledStyle property to determine how to fill the rectangle.
    1. ctx.fillStyle='pink';
    2. ctx.fillRect(10,10,200,200);
    Produce this result:
    filled rectangle

    strokeStyle is a property of the context similar to fillStyle, but this time for indicating how the shape's outline should be rendered.

    The possible values are the same as those for the fillStyle property: a color, a pattern, or a gradient. This property will be taken into account when wireframe shapes are drawn.

    strokeRect(x, y, width, height): like fillRect(...), but instead of drawing a filled rectangle the rectangle is drawn in wireframe mode.

    1. ctx.strokeStyle='blue';
    2. ctx.strokeRect(10,10,200,200);
    ...produces this result:
    stroked rectangle
    Only the outline of the rectangle will be drawn, and it will be drawn using the value of the strokeStyleproperty.

    clearRect(x, y, width, height): a call to this method erases the specified rectangle.

    Actually it draws it in a color called "transparent black" (!) that corresponds to the initial state of the rectangle as if no drawing had occurred.
    1. ctx.fillStyle='pink';
    2. ctx.fillRect(10,10,200,200);
    3. ctx.clearRect(50, 50, 20, 20);
    The result is:
    clear rect

    LET'S SEE SOME SIMPLE EXAMPLES...

    Draw a wireframe red rectangle, width lineWidth = 3 pixels.

    wireframe red rectangle with line width = 3 pixels
    Extract from the source code (the part that draws the rectangle):
    1. function drawSomething() {
    2.      // draw a red rectangle, line width=3 pixels
    3.      ctx.lineWidth=3;
    4.      ctx.strokeStyle='red';
    5.      ctx.strokeRect(10,10,80,100);
    6. }
    Here, we used "stroke" instead of "fill" in the property and method names (lines 4 and 5): strokeStyle instead of fillStylestrokeRect(...) instead of fillRect(...).
    We also introduced a new property of the context, that applies only when drawing in "stroke" mode, thelineWidth property (line 3), that is used for setting the width of the shape outline. The value is in pixels.

    DRAW 2 FILLED RED RECTANGLES WITH A BLUE OUTLINE OF 5 PIXELS AND SOME TEXT

    Let's continue with another example. This time we will draw several shapes that share the same colors, they will be filled in red, with a blue outline. We also show how to draw a text message with a given font.
    rectangles and text that shares colors
    Source code extract:
    1. function drawSomething() {
    2.      // set the global context values
    3.     ctx.lineWidth=5;
    4.     ctx.fillStyle='red';
    5.     ctx.strokeStyle='blue'
    6.     // font for all text drawing
    7.     ctx.font = 'italic 20pt Calibri';
    8.     // Draw the two filled red rectangles
    9.     ctx.fillRect(10, 30, 70, 150);
    10.     ctx.fillRect(110, 30, 70, 150);
    11.     // Draw the two blue wireframe rectangles
    12.     ctx.strokeRect(10, 30, 70, 150);
    13.     ctx.strokeRect(110, 30, 70, 150);
    14.     // Draw a message above the rectangles
    15.     ctx.fillText("hello", 70, 22);
    16. }
    This example shows the "global" nature of the context properties. Once you set the filled color to red, any shapes you draw in filled mode will be red. This is true for all the context properties. We set in lines 3-7 some of these properties, and all following calls to context methods for drawing rectangles or text will depend on them. The two filled rectangles at lines 10-11 will be red, the two wireframe rectangles drawn at lines 14-15 will be blue, etc.
    Line 18 shows how to draw a text message at an X position of 70 and a Y position of 22. The font is set at line 7 using the font property of the context.  The syntax is the same we use in CSS for using "system fonts".
    If you would like to draw the filled text message in green for example, you should set the ctx.fillStyleproperty to "green" after you draw the rectangles and before you draw the text (i.e just before line 18).

    SUMMARY OF WHAT WE LEARNED

    • "stroke" means "wireframe" or "outlined", it is a prefix for setting properties or calling methods that will affect wireframe shapes, "fill" is a prefix for filled shapes.
    • To set the properties of wireframe shapes use ctx.strokeStyle= ..., for filled shapes usectx.fillStyle=... So far the values we have used are colors, expressed as strings. Example:ctx.strokeStyle  = 'blue';
    • To draw a wireframe rectangle use ctx.strokeRect(x, y, width, height), to draw a filled rectangle use ctx.fillRect(x, y, width, height);
    • To set the line width of wireframe shapes, use the ctx.lineWidth property. Example ctx.lineWidth = 10; ctx.strokeRect(0, 0, 100, 100);  will draw a 100x100 rectangle in wireframe mode, with an outline width of 10 pixels.
    • To draw a text message use ctx.strokeText(message, x, y) or ctx.fillText(message, x, y), for wireframe text or filled text respectively.
    • To set the character font use the ctx.font property; the value is a font in CSS syntax, for example:  ctx.font = 'italic 20pt Calibri';

    2D transformations: changing the coordinate system

    INTRODUCTION

    In this part of the course, we introduce the basics of 2D transformations, a powerful tool that will make things easier as soon as you have to:
      • Draw complex shapes at given positions, with given orientations and sizes,
      • Draw shapes relative to one another.
    Let's start with some simple examples before looking at how we use 2D transforms.

    LET'S DRAW THREE RECTANGLES!

    If we draw three rectangles of size 100x200 in a 400x400 canvas, one at (0, 0) and another one at (150, 0), and a third one at (300, 0), here is the result and the corresponding code.
    3 green rectangles
    JavaScript code extract (see the online example at JS Bin for the complete running code)
    1. function drawSomething() {
    2.      ctx.fillStyle='lightgreen';
    3.  
    4.      ctx.fillRect(0,0,100,200);
    5.      ctx.fillRect(150,0,100,200);
    6.      ctx.fillRect(300,0,100,200);
    7. }

    LET'S MODIFY THE CODE SO THAT WE CAN DRAW THESE RECTANGLES AT ANY X AND Y POSITION

    What if we wanted to draw these 3 rectangles at another position, as a group? We would like to draw all of them a little more towards the bottom, for example... Let's add some parameters to the function:  the X and Y position where we're going to draw the rectangles.
    The full JavaScript code is (see online running example):
    1. var canvas, ctx;
    2. function init() {
    3.     // This function is called after the page is loaded
    4.     // 1 - Get the canvas
    5.     canvas = document.getElementById('myCanvas');
    6.     // 2 - Get the context
    7.     ctx=canvas.getContext('2d');
    8.     // 3 - we can draw
    9.     drawSomething(0, 100);
    10. }
    11. function drawSomething(x, y) {
    12.     // draw 3 rectangles
    13.     ctx.fillStyle='lightgreen';
    14.     ctx.fillRect(x,y,100,200);
    15.     ctx.fillRect(x+150,y,100,200);
    16.     ctx.fillRect(x+300,y,100,200);
    17. }
    At line 10, we called the drawSomething(...) function with 0 and 100 as parameters, meaning "please add an offset of 0 in X and 100 in Y directions to what is drawn by the function...
    If you look at the code of the modified function, you will see that each call to fillRect(...) uses the x and y parameters instead of hard coded values. Like that, if we call it with parameters (0, 100), then all rectangles will be drawn 100 pixels to the bottom (offset in y). Here is the result:
    rectangles are drawn 100 pixels towards the bottom

    OK, NOW LET'S DRAW A SMALL MONSTER'S HEAD WITH RECTANGLES

    Now we can start having some fun... let's draw a monster's head, only with rectangles (online version):
    monster made of rectangles
    An extract of the JavaScript source code is:
    1. function drawMonster(x, y) {
    2.    // head
    3.    ctx.fillStyle='lightgreen';
    4.    ctx.fillRect(x,y,200,200);
    5.    // eyes
    6.    ctx.fillStyle='red';
    7.    ctx.fillRect(x+35,y+30,20,20);
    8.    ctx.fillRect(x+140,y+30,20,20);
    9.    // interior of eye
    10.    ctx.fillStyle='yellow';
    11.    ctx.fillRect(x+43,y+37,10,10);
    12.    ctx.fillRect(x+143,y+37,10,10);
    13.    // Nose
    14.    ctx.fillStyle='black';
    15.    ctx.fillRect(x+90,y+70,20,80);
    16.    // Mouth
    17.    ctx.fillStyle='purple';
    18.    ctx.fillRect(x+60,y+165,80,20);
    19. }
    As you can see, the code uses the same technique, but starts becoming less and less readable. The Xs and Ys at the beginning of each call makes understanding the code harder, etc.
    However, there is a way to simplify this => 2D geometric transformations! 

    GEOMETRIC TRANSFORMATIONS: CHANGING THE COORDINATE SYSTEM

    The idea behind 2D transformations is that instead of modifying all the coordinates passed as parameters to each call to drawing methods like fillRect(...), we will keep all the drawing code "as is". For example, if the monster of our previous example was drawn at (0, 0), we could just translate (or rotate, or scale) the original coordinate system.
    Let's take a piece of code that draws something corresponding to the original coordinate system, located at the top left corner of the canvas:
    1. function drawMonster(x, y) {
    2.    // head
    3.    ctx.fillStyle='lightgreen';
    4.    ctx.fillRect(0,0,200,200);
    5.    // eyes
    6.    ctx.fillStyle='red';
    7.    ctx.fillRect(35,30,20,20);
    8.    ctx.fillRect(140,30,20,20);
    9.    // interior of eye
    10.    ctx.fillStyle='yellow';
    11.    ctx.fillRect(43,37,10,10);
    12.    ctx.fillRect(143,37,10,10);
    13.    // Nose
    14.    ctx.fillStyle='black';
    15.    ctx.fillRect(90,70,20,80);
    16.    // Mouth
    17.    ctx.fillStyle='purple';
    18.    ctx.fillRect(60,165,80,20);
    19.    // coordinate system at (0, 0)
    20.    drawArrow(ctx, 0, 0, 100, 0, 10, 'red');
    21.    drawArrow(ctx, 0, 0, 0, 100, 10, 'red');
    22. }
    This code is the just the same as in the previous example except that we removed all Xs and Yx in the code. We also added at the end (lines 25-26) two lines of code that draw the coordinate system. ThedrawArrow(startX, startY, endX, endY, width, color) function is a utility function that we will present later. You can see it in the source code of the complete online example on JS Bin: look in the JavaScript tab.
    Note that the X and Y parameters are useless for now...
    Result:
    monster at 0, 0, coordinate system is drawn too as two arrows

    Translation using ctx.translate(offsetX, offsetY)

    Now, instead of simply calling drawMonster(0, 0), we will call first ctx.translate(100, 100), and look at the result (online code: http://jsbin.com/yuhamu/2/edit)
    translated monster
    JavaScript code extract:
    1. ctx.translate(100, 100);
    2. drawMonster(0, 0);
    Line 1 changes the position of the coordinate system, line 2 draws a monster in the new translated coordinate system. All subsequent calls to drawing methods will be affected and will work in this new system too.

    OTHER TRANSFORMATIONS: ROTATE, SCALE

    There are other transformations available:
    • ctx.rotate(angle), with angle in radians. Note that the order of transformations is important, usually we translate, then rotate, then scale... If you change this order, you need to know what you are doing...
    • ctx.scale (sx, sy), where scale(1, 1) corresponds to "no zoom", scale(2, 2) corresponds to "zooming 2x" and scale(0.5, 0.5) corresponds to zooming out to see the drawings half as big as before. If you do not use the same values for sx and sy, you do "asymmetric scaling", you can distort a shape horizontally or vertically. Try changing the values in the source code of the next online examples.
    Here is the previous example, but this time we translated the coordinate system, then rotated it with an angle equal to PI/4 , then we scaled it so that units are half as big (see the example online):
    coordinate system translated, rotated and scaled
    And here is the code of the transformations we used, followed by the call to the function that draws the monster:
    1. ctx.translate(100, 100);
    2. ctx.rotate(Math.PI/4);
    3. ctx.scale(0.5, 0.5);
    4.  
    5. drawMonster(0, 0);
    Line 1 does:
    translate coordinate system
    Line 2 does:
    rotate coordinate system
    Line 3 does:
    scale coordinate system
    Line 4 draws the monster in this new translated, rotated, scaled coordinate system:
    coordinate system translated, rotated and scaled

    BEWARE: ALL DRAWINGS TO COME WILL BE IN THAT MODIFIED COORDINATE SYSTEM!

    If we draw two shapes at two different positions, they will be relative to this new coordinate system.
    For example, this code (online at http://jsbin.com/yuhamu/4/edit): 
    1. ctx.translate(100, 100);
    2. ctx.rotate(Math.PI/4);
    3. ctx.scale(0.5, 0.5);
    4. // Draw the monster at (0, 0)
    5. drawMonster(0, 0);
    6. // Draw a filled rectagle at (250, 0)
    7. ctx.fillRect(250, 0, 100, 100);
    ... gives this result (there is an error in the red text below, it should say "x=250, y=0"):
    monster and rectangle in the same coordinate system

    HOW CAN WE RESET THE COORDINATE SYSTEM, HOW CAN WE GO BACK TO THE PREVIOUS ONE?

    Aha, this is a very interesting question... we will give the answer very soon in the section on saving/restoring the context :-)

    Knowledge check 3.2.8 (not graded)

    ctx.translate(100, 100);
    ctx.scale(2, 2);
    ctx.strokeRect(0, 0, 100, 100);

Saving and restoring the context

There are two methods for saving and restoring the context properties: ctx.save()and ctx.restore().
What will be saved: fillStyle and strokeStylelineWidth, previous coordinate system, etc., that is ALL properties that affect drawing!
A call to ctx.save() will probably save the context property values in a hardware register on your graphics card. Multiple contexts can be saved consecutively and restored.
Contexts saved will be stacked, the last one that has been saved will be restored by the next call to restore(), so it is very important to have one restore for each save.
Best practice: save the context at the beginning of any function 
that changes the context, restore it at the end of the function!

EXAMPLE OF A FUNCTION THAT CHANGES THE CONTEXT AND RESTORES IT AFTER EXECUTION

example of context save / restore : a monster is drawn by a function that saves and restored the context, then a rectangle is draw, with context as it was previously
We took the last example we saw (the one with the monster, from the previous page of the course), and slightly modified the function that draws the monster:
    • We added parameters for setting the position and orientation of the monster, and added calls to ctx.translate(x, y) and ctx.rotate(angle) in the function.
    • We added parameters for the head's color and eyes' color.
    • We saved the context at the beginning of the function (BEST PRACTICE),
    • We restored it at the end (BEST PRACTICE).
Source code extract of this function: notice at lines 3 and 26 how we save/restore the context at the beginning/end. Right after saving the context, we modify the coordinate system (lines 7-8). The rest of the code is nearly the same as in the last version of the monster example.
  1. function drawMonster(x, y, angle, headColor, eyeColor) {
  2.     // BEST PRACTICE : SAVE CONTEXT AND RESTORE IT AT THE END
  3.     ctx.save();
  4.     // Moves the coordinate system so that the monster is drawn
  5.     // at position (x, y)
  6.     ctx.translate(x, y);
  7.     ctx.rotate(angle);
  8.     // head
  9.     ctx.fillStyle=headColor;
  10.     ctx.fillRect(0,0,200,200);
  11.     // eyes
  12.     ctx.fillStyle='red';
  13.     ctx.fillRect(35,30,20,20);
  14.     ctx.fillRect(140,30,20,20);
  15.     // interior of eye
  16.     ctx.fillStyle=eyeColor;
  17.     ctx.fillRect(43,37,10,10);
  18.     ctx.fillRect(143,37,10,10);
  19.  
  20.     ...
  21.     // BEST PRACTICE !
  22.     ctx.restore();
  23. }


4 comments:

  1. At Neinstein Personal Injury Lawyers, we pride ourselves on being successful. You will not pay any legal fees until your case is successfully settled. We operate on what is typically called a contingency fee (percentage of recovery) basis. The percentage that applies to your case depends on the type of case. This information will be explained to you from the outset and will form part of our Retainer Agreement.

    personal injury lawyers
    birth injury lawyer Toronto
    accident lawyer
    toronto car accident lawyer
    spinal cord injury lawyers
    Hamilton Car Accident Lawyers


    ReplyDelete
  2. Thanks for your informative post. This content is really helpful and works for me. We are also serving personal injury and brain injury lawyer services in oakville. And at the end of my comment, i just want to say that your posts are very good to keep updating us.

    oakville personal injury lawyers
    injury lawyer in oakville
    brain injury lawyer

    ReplyDelete