Canvas context: colors

Canvas context: colors

COLORS AND TRANSPARENCY

You can use the same syntax for colors that is supported by CSS3. The next lines show possible values/syntaxes.

  1. ctx.strokeStyle = ‘red’;
  2. ctx.fillStyle = “#00ff00”;
  3. ctx.strokeStyle = “rgb(0, 0, 255)”;
  4. ctx.fillStyle = “rgba(0, 0, 255, 0.5)”;

Note that:

    • All values are strings,
    • Line 4 defines a “transparent color”, the “a” of “rgba” means “alpha channel”. Its value is between 0 and 1, where 0 means “completely transparent” and 1 means “opaque”.

Here is an example that shows how to draw different filled rectangles in blue, with different levels of transparency.

Try it online: http://jsbin.com/duwaxa/3/edit

transparent rgba color example

Linear gradients

It is possible to define the stroke or the fill style as a “gradient”, a set of interpolated colors, like in this example below (try it online at: http://jsbin.com/ruguxi/2/edit)

linear gradient from blue to white to red

The concept of linear gradient is seen as an “invisible” rectangle in which a set of colors are interpolated along a line.

The gradient becomes visible when we draw shapes on top of the invisible gradient, and when the fillStyleor strokeStyle property has for value this gradient.

Here are the different steps needed:

Step 1: define a linear gradient

Syntax:

  1. ctx.createLinearGradient(x0,y0,x1,y1);

… where the (x0, y0) and (x1, y1) parameters define “the direction of the gradient” (as a vector with a starting and an ending point). This direction is an invisible line along which the colors that compose the gradient will be interpolated.

Let’s see an example:

  1. grdFrenchFlag = ctx.createLinearGradient(0, 0, 300, 0);

This line defines the direction of the gradient: a virtual, invisible line that goes from the top left corner of the canvas (0, 0) to the top right corner of the canvas (300, 0). The interpolated colors will propagate along this line.

If this gradient is going to be reused by different functions, it is good practice to create/initialize it in a function called when the page is loaded and to store it in a global variable.

Step 2: add a number of “color stops” to this gradient

We will add a set of “colors” and “stops” to this gradient. The stops go from 0 (beginning of the virtual line defined just above), to 1 (end of the virtual line). A color associated with a value of 0.5 will be right in the middle of the virtual line.

Here is an example that corresponds to an interpolated version of the French flag, going from blue to white, then to red, with proportional intervals. We define three colors, blue at position 0, white at position 0.5 and red at position 1:

  1. grdFrenchFlag.addColorStop(0, “blue”);
  2. grdFrenchFlag.addColorStop(0.5, “white”);
  3. grdFrenchFlag.addColorStop(1, “red”);

Step 3: draw some shapes

First, let’s set the fillStyle or strokeStyle of the context with this gradient, then let’s draw some shapes “on top of the gradient”.

In our example, the gradient corresponds to an invisible rectangle that fills the canvas. If we draw a rectangle of the canvas size, it should be filled with the entire gradient:

  1. ctx.fillStyle = grdFrenchFlag;
  2. ctx.fillRect(0, 0, 300, 200);

The result is shown below: a big rectangle that fills the whole canvas, with colors going from blue (left) to white (middle) to red (right).

linear gradient from blue to white to red, left to right

CHANGING THE DIRECTION OF THE GRADIENT

If you modify the source code that defines the direction of the gradient as follows…

  1. grdFrenchFlag = ctx.createLinearGradient(0, 0, 300, 200);

… then you will define a gradient that goes from the top left corner of the canvas to the bottom right of the canvas. Let’s see what it does (online version here: http://jsbin.com/ruguxi/4/edit):

diagonal gradient from top left to bottom right

DRAWING SHAPES THAT DO NOT COVER THE WHOLE GRADIENT

Instead of drawing a filled rectangle that covers the whole surface of the canvas, let’s draw several smaller rectangles.

Online example: http://jsbin.com/baluxa/3/edit

smaller rectangle on top of a gradient

Note that the canvas has its default background color where we did not draw anything. And where we have drawn rectangles, we can see “through” and the colors from the gradient are visible.

Here is the code that draws the checkboard:

  1. ctx.fillStyle = grdFrenchFlag;
  2. ctx.fillRect(0, 0, 50, 50);
  3. ctx.fillRect(100, 0, 50, 50);
  4. ctx.fillRect(200, 0, 50, 50);
  5. ctx.fillRect(50, 50, 50, 50);
  6. ctx.fillRect(150, 50, 50, 50);
  7. ctx.fillRect(250, 50, 50, 50);
  8. ctx.fillRect(0, 100, 50, 50);
  9. ctx.fillRect(100, 100, 50, 50);
  10. ctx.fillRect(200, 100, 50, 50);
  11. ctx.fillRect(50, 150, 50, 50);
  12. ctx.fillRect(150, 150, 50, 50);
  13. ctx.fillRect(250, 150, 50, 50);

This code is rather ugly isn’t it? It would have been better  to use a loop…

Here is function that draws a chessboard (online example at JsBin):

  1. // n = number of cells per row/column
  2. function drawCheckboard(n) {
  3.     ctx.fillStyle = grdFrenchFlag;
  4.     var l = canvas.width;
  5.     var h = canvas.height;
  6.  
  7.     var cellWidth = l / n;
  8.     var cellHeight = h / n;
  9.     for(i = 0; i < n; i++) {
  10.        for(j = i % 2; j < n; j++) {
  11.           ctx.fillRect(cellWidth*i, cellHeight*j, cellWidth, cellHeight);
  12.        }
  13.     }
  14. }

The two loops (lines 11-15) draw only one cell out of two (see the j = i % 2 at line 12). i is the column number and if the column is odd or even, either we draw or we do not draw a rectangle.

This code is much more complex than the previous one, taking 16 lines instead of 13, but is much more powerful. Try to call the function with a value of 10, 20, or 2…

DRAWING OUTLINED SHAPES WITH GRADIENTS

Just as we used fillStyle and fillRect for drawing rectangles filled with a gradient, we can also use strokeStyle and strokeRect in order to draw wireframed rectangles. In the next example, which is just a variation of the previous one, we have used the lineWidth property to set the outline of the rectangles at 5 pixels: try this example at JsBin.

stroke rectangles with gradient

Extract from source code:

  1. function drawCheckboard(n) {
  2.     ctx.strokeStyle = grdFrenchFlag;
  3.     ctx.lineWidth=10;
  4.     …
  5.     for(i = 0; i < n; i++) {
  6.         for(j = i % 2; j < n; j++) {
  7.             ctx.strokeRect(cellWidth*i, cellHeight*j, cellWidth, cellHeight);
  8.         }
  9.     }
  10. }

WHAT HAPPENS IF WE DEFINE A GRADIENT SMALLER THAN THE CANVAS?

Let’s go back to the very first example on this page – the one with the blue-white-red interpolated French flag. This time we will define a smaller gradient. Instead of going from (0, 0) to (300, 0), it will go from (100, 0) to (200, 0), while the canvas remains the same (width=300, height=200).

  1. grdFrenchFlag = ctx.createLinearGradient(100, 0, 200, 0);

Like in the first example we will draw a filled rectangle that is the same size as the canvas. Here is the online version: http://jsbin.com/ruvuta/1/edit, and here is a screenshot of the result:

gradient smaller thant the drawn shape

We notice that “before” the gradient starts, the first color of the gradient is repeated without any interpolation (columns 0-100 are all blue), then we “see through” and the gradient is drawn (columns 100-200), then the last color of the gradient is repeated without any interpolation (columns 200-300 are red).

WHAT HAPPENS IF WE DEFINE A GRADIENT BIGGER THAN THE CANVAS?

Nothing special; we will “see through the drawn shapes”, and the parts of the gradient that are located in the canvas area will be shown. You can try this example that defines a gradient twice the size of the canvas:

  1. grdFrenchFlag = ctx.createLinearGradient(0, 0, 600, 400);

And if we draw the same rectangle with the canvas size, here is the result:

gradient bigger than the canvas size

The red color is beyond the bottom right corner…. we see only the top left quarter of the gradient.

DRAW SHAPES THAT SHARE THE SAME GRADIENT AS A WHOLE

This time, we would like to draw the chessboard with the gradient in each cell. How can we do this with one single gradient?

We can’t! At least we can’t without recreating it for each cell!

It suffices to create a new gradient before drawing each filled rectangle, and set it with the starting and ending point of its direction/virtual line accordingly to the rectangle coordinates. Here is an online example and the resulting display:

chessboard with individual gradient for each cell

Extract from source code:

  1. function setGradient(x, y, width, height) {
  2.     grdFrenchFlag = ctx.createLinearGradient(x, y, width, height);
  3.     grdFrenchFlag.addColorStop(0, “blue”);
  4.     grdFrenchFlag.addColorStop(0.5, “white”);
  5.     grdFrenchFlag.addColorStop(1, “red”);
  6.     // set the new gradient to the current fillStyle
  7.     ctx.fillStyle = grdFrenchFlag;
  8. }
  9.  
  10. // n = number of cells per row/column
  11. function drawCheckboard(n) {
  12.    var l = canvas.width;
  13.    var h = canvas.height;
  14.  
  15.    var cellWidth = l / n;
  16.    var cellHeight = h / n;
  17.    for(i = 0; i < n; i+=2) {
  18.      for(j = 0; j < n; j++) {
  19.         var x = cellWidth*(i+j%2);
  20.         var y = cellHeight*j;
  21.         setGradient(x, y, x+cellWidth, y+cellHeight);
  22.         ctx.fillRect(x, y, cellWidth, cellHeight);
  23.      }
  24.    }
  25. }

We wrote a function setGradient(startX, startY, endX, endY) that creates a gradient and set the fillStyle context property so that any filled shape drawn will have this gradient.

In the drawCheckBoard(…) function we call it just before drawing rectangles. In this way, each rectangle is drawn using its own gradient.

THIS FLAG DOES NOT REALLY LOOK LIKE THE FRENCH FLAG, DOES IT?

Indeed the French flag in these images is more accurate:

french flag with linear gradient

french flags with linear gradient

We slightly modified the examples of this chapter so that the flag looks more like the French flag. Look at the modified versions below, and try to find out what has changed in the gradient definitions:

Canvas context: radial gradients

Radial gradients

BASIC PRINCIPLE / SYNTAX: DEFINE TWO CIRCLES AT GRADIENT CREATION

Radial gradients are for creating gradients that propagate/interpolate colors along circles instead of propagating/interpolating along a virtual line, like linear gradients.

Here is an example of a radial gradient that interpolates the color of the rainbow. Online version: http://jsbin.com/mijoni/2/edit

radial gradient example: circles with the rainbow colors

The gradient is defined as follows:

  1. var grd = context.createRadialGradient(150, 100, 30, 150, 100, 100);
  2. grd.addColorStop(0, “red”);
  3. grd.addColorStop(0.17, “orange”);
  4. grd.addColorStop(0.33, “yellow”);
  5. grd.addColorStop(0.5, “green”);
  6. grd.addColorStop(0.666, “blue”);
  7. grd.addColorStop(1, “violet”);
  8. context.fillStyle = grd;

The method from the context object createRadialGradient(cx1, cy1, radius1, cx2, cy2, radius2) takes as the first three parameters the “starting” circle of the gradient, and as the three last parameters, the “ending circle”.

In the above example, the gradients starts at a circle located at (150, 100), with a radius of 30, and propagates to a circle with the same center as the first (150, 100), but with a bigger radius of 100, as shown below:

//d37djvu3ytnwxt.cloudfront.net/assets/courseware/v1/b83edcaf723d02f063cc50082fae441e/asset-v1:W3Cx+HTML5.1x+3T2016+type@asset+block/radialgradient2.jpg

We added color stops using a method similar to that used for linear gradients.

WHAT HAPPENS IF THE CIRCLES ARE NOT LOCATED AT THE SAME PLACE?

You obtain some nice effects; here we set the second circle’s center 60 pixels to the right of the first circle’s center (cx = 210 instead of 150). Online example: http://jsbin.com/fufelef/1/edit?html

  1. grd = ctx.createRadialGradient(150, 100, 30, 210, 100, 100);

And here is the result:

radial gradient with circles non aligned

WHAT HAPPENS IF THE GRADIENT IS SMALLER OR LARGER THAN THE SHAPES WE DRAW?

A gradient is an invisible shape on the screen: the radial gradient is made of two circles: an inner and an outer circle. Between these two circles, colors are interpolated.

We call the “first color” the color defined for the inner circle, the “last color” the last color of the gradient, that corresponds to the outer circle:

    • The color inside the first circle will be the first color.  In our example above, the first color is red: and the small circle of the gradient in our example is filled in red!
    • The color outside the outer circle will be the last color of the gradient – which is not interpolated. The last color in our example is purple, and it fills the rest of the filled rectangle area “after” the external circle of the gradient.
    • The colors between the two circles will be interpolated.

Painting with patterns

PRINCIPLE

The principle of “pattern” drawing is based on repeating an image (if the image is smaller than the surface of the shape you are going to draw) for filling the surface of objects to be drawn (either filled or stroked).

To illustrate this principle, in the next examples, we are going to draw rectangles using this pattern:

an example of repeateable pattern

There are a few steps we have to take before doing this:

    1. Create a JavaScript image object
      1. var imageObj = new Image();
    2. Define a callback function that will be called once the image has been fully loaded in memory; we cannot draw before the image has been loaded.
      1. imageObj.onload = function(){
      2. }
    3. Set the source of this image to the URL of the pattern (in our example with url of the pattern),
    4. As soon as step 3 is executed, an HTTP request is sent in background by the browser, and when the image is loaded in memory, the callback defined at step 2 is called. We create a pattern object inside, from the loaded image:
      1. // callback called asynchronously, after the src attribute of imageObj is set
      2. imageObj.onload = function(){ 
      3.     // We enter here when the image is loaded, we create a pattern object.
      4.     // It is good practice to set this as a global variable, easier to share
      5.     pattern1 = ctx.createPattern(imageObj, “repeat”);
      6. };
    5. Inside the callback function (or inside a function called from inside the callback) we can draw.
      1. // callback called asynchronously, after the src attribute of imageObj is set
      2. imageObj.onload = function(){
      3.     pattern1 = ctx.createPattern(imageObj, “repeat”);
      4.     // Draw a textured rectangle
      5.     ctx.fillStyle = pattern1;
      6.     ctx.fillRect(10, 10, 500, 800);
      7. };

EXAMPLE 1: DRAW TWO RECTANGLES WITH A PATTERN (ONE FILLED, ONE STROKED)

Online example: http://jsbin.com/qezojo/1/edit

Here we have two rectangles drawn using a pattern (an image that can be repeated along the X and Y axis). The first is a filled rectangle while the second is “stroked” with a lineWidth of 10 pixels.

example of painting with patterns

HTML source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <body onload=init();>
  4.    <canvas id=“myCanvas” width=“500” height=“400”>
  5.        Your browser does not support the canvas tag. </canvas>
  6.    </body>
  7. </html>

JavaScript source code:

  1. var canvas, ctx, pattern1;
  2.  
  3. function init() {
  4.    canvas = document.querySelector(‘#myCanvas’);
  5.    ctx = canvas.getContext(‘2d’);
  6.    // We need 1) to create an empty image object, 2) to set a callback function
  7.    // that will be called when the image is fully loaded, 3) to create a
  8.    // pattern object, 4) to set the fillStyle or the strokeStyle property of
  9.    // the context with this pattern, 5) to draw something
  10.    // WE CANNOT DRAW UNTIL THE IMAGE IS FULLY LOADED -> draw from inside the
  11.    // onload callback only !
  12.    // 1 – Allocate an image
  13.    var imageObj = new Image();
  14.  
  15.    // 2 – callback called asynchronously, after the src attribute of imageObj
  16.    // is set
  17.    imageObj.onload = function(){
  18.       // We enter here only when the image has been loaded by the browser
  19.       // 4 – Pattern creation using the image object
  20.       // Instead of “repeat”, try different values : repeat-x, repeat-y,
  21.       // or no-repeat, You may draw larger shapes in order to see
  22.       // different results
  23.       // It is good practice to leave this as a global variable if it
  24.       // will be reused by other functions
  25.       pattern1 = ctx.createPattern(imageObj, “repeat”);
  26.       // 5 – Draw things. Here a textured rectangle
  27.       ctx.fillStyle = pattern1;
  28.       ctx.fillRect(10, 10, 200, 200);
  29.       // … And a wireframe one
  30.       ctx.lineWidth=20;
  31.       ctx.strokeStyle=pattern1;
  32.       ctx.strokeRect(230, 20, 150, 100);
  33.   };
  34.   // 3 – Send the request to load the image
  35.   // Setting the src attribute will tell the browser to send an asynchronous
  36.   // request.
  37.   // When the browser gets an answer, the callback above will be called
  38.   imageObj.src = http://www.dreamstime.com/colourful-flowers-repeatable-pattern-thumb18692760.jpg&#8221;;
  39. }

EXAMPLE 2: THE REPEATABILITY OF A PATTERN

To “better” see the repeatability of the pattern, here is the same example with a 1000×1000 pixel wide canvas.

Online version here: http://jsbin.com/befiti/3/edit, and here is the result:

same example with bigger canvas and bigger rectangles

You can change the way the pattern is repeated by modifying the second parameter of this method:

  1. pattern1 = ctx.createPattern(imageObj, repeat);

Multiple image loader (for advanced users, or just take it as it is: a utility function you can reuse)

Below are 4 rectangles drawn with 4 different patterns.

4 rectangles drawn with different patterns

DRAW WITH MULTIPLE PATTERNS? WE NEED TO LOAD ALL OF THEM BEFORE DRAWING!

We said earlier that we cannot draw before the image used by a pattern is loaded. This can become rapidly complicated if we need to draw using multiple patterns. We need a way to load all images and then, only when all images have been loaded, start drawing.

JavaScript is an asynchronous language. When you set the src attribute of an image, then an asynchronous request is sent by the browser, and then after a while, the onload callback is called… The difficult part to understand for those who are not familiar with JavaScript is that these requests are done in parallel and we do not know when, and in what order, the images will be loaded.

SOLUTION: A MULTIPLE IMAGE LOADER THAT COUNTS THE LOADED IMAGES AND CALLS A FUNCTION YOU PASS WHEN DONE!

The trick is to have an array of URLs that will be used by our multiple image loader, then in the onload callback, this will be called once per image loaded, so we can count the number of images effectively loaded.

When all images have been loaded, we call a callback function that has been passed to our loader.

A complete example code that produces the result shown at the beginning of this page is at: http://jsbin.com/fufiyu/6/edit

Define the list of images to be loaded

  1. // List of images to load, we used a JavaScript object instead of
  2. // an array, so that named indexes (aka properties)
  3. // can be used -> easier to manipulate
  4. var imagesToLoad = {
  5.      flowers:
  6.         ‘http://cdn2.digitalartsonline.co.uk/images/features/1675/intro.jpg&#8217;,
  7.      lion: http://farm3.static.flickr.com/2319/2486384418_8c031fec76_o.jpg&#8217;,
  8.      blackAndWhiteLys: http://pshero.com/assets/tutorials/0062/final.jpg&#8217;,
  9.      tiledFloor:
  10.       ‘http://4.bp.blogspot.com/_Rqs7w7m37B4/TETj5rD_QmI/AAAAAAAAADk/qRiwoTO-zKk/s1600/symmetry:assymetry+repeatable+pattern.jpg&#8217;
  11. };

Notice that instead of using a traditional array, we defined this list as a JavaScript object, with properties whose names will be easier to manipulate (flowers, lion, tiledFloor, etc.).

The image loader function

  1. function loadImages(imagesToBeLoaded, drawCallback) {
  2.      var imagesLoaded = {};
  3.      var loadedImages = 0;
  4.      var numberOfImagesToLoad = 0;
  5.      // get num of images to load
  6.      for(var name in imagesToBeLoaded) {
  7.          numberOfImagesToLoad++;
  8.      }
  9.      for(var name in imagesToBeLoaded) {
  10.          imagesLoaded[name] = new Image();
  11.          imagesLoaded[name].onload = function() {
  12.              if(++loadedImages >= numberOfImagesToLoad) {
  13.                  drawCallback(imagesLoaded);
  14.              } // if
  15.          }; // function
  16.          imagesLoaded[name].src = imagesToBeLoaded[name];
  17.       } // for
  18. } // function

This function takes as a parameter the list of images to be loaded, and a drawCallback function that will be called only once all images have been loaded. This callback takes as a parameter a new object that is the list of images that have been loaded (see line 16).

We first count the number of images to load (lines 7-9), then for each image to be loaded we create a new JavaScript image object (line 12) and set its src attribute (line 19) – this will start to load the image.

When an image comes in, the onload callback is called (line 14) and inside, we increment the number of images loaded (line 15) and test if this number is >=  the total number of images that should be loaded. If this is the case, the callback function is called (line 16).

Example of use of this loader

  1. loadImages(imagesToLoad, function(imagesLoaded) {
  2.     patternFlowers = ctx.createPattern(imagesLoaded.flowers, ‘repeat’);
  3.     patternLion    = ctx.createPattern(imagesLoaded.lion, ‘repeat’);
  4.     patternBW = ctx.createPattern(imagesLoaded.blackAndWhiteLys, ‘repeat’);
  5.     patternFloor   = ctx.createPattern(imagesLoaded.tiledFloor, ‘repeat’);
  6.     drawRectanglesWithPatterns();
  7. });
  • Line 1 is the call to the image loader, the first parameter is the list of images to be loaded, while the second parameter is the callback function that will be called once all images have been loaded.
  • Lines 2-5: in this callback we create patterns from the loaded images (note the use of the property names imagesLoaded.flowers, etc. that makes the code easier to read).
  • Line 7: then we call a function that will draw the rectangles.

Here is the function:

  1. function drawRectanglesWithPatterns() {
  2.     ctx.fillStyle=patternFloor;
  3.     ctx.fillRect(0,0,200,200);
  4.     ctx.fillStyle=patternLion;
  5.     ctx.fillRect(200,0,200,200);
  6.     ctx.fillStyle=patternFlowers;
  7.     ctx.fillRect(0,200,200,200);
  8.  
  9.     ctx.fillStyle=patternBW;
  10.     ctx.fillRect(200,200,200,200);
  11. }

Drawing shadows

CONTEXT PROPERTIES TO DRAW WITH SHADOWS

a shadowed rectangle

There are 4 properties of the canvas context that are useful for indicating that we want to draw shapes with shadows:

    1. shadowColor: color to use for shadows,
    2. shadowBlur: blur level for shadows,
    3. shadowOffsetX: horizontal distance of the shadow from the shape,
    4. shadowOffsetY: vertical distance of the shadow from the shape

EXAMPLE 1: SIMPLE

Online example: http://jsbin.com/wivubi/3/edit

two shadowed rectangles

HTML source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <body onload = init();>
  4.     <canvas id=“myCanvas” width=“400” height =800>
  5.         Your browser does not support the canvas tag.
  6. </canvas>
  7. </body>
  8. </html>

JavaScript source code:

  1. var canvas, ctx;
  2. function init() {
  3.     canvas = document.getElementById(‘myCanvas’);
  4.     ctx = canvas.getContext(‘2d’);
  5.     // call to a function that will set the 4 context properties for shadows
  6.     setShadow();
  7.     // all drawings that will occur will cast shadows
  8.     // first green filled rectangle
  9.     ctx.fillStyle = “#22FFDD”; 
  10.     ctx.fillRect(20, 20, 200, 100);
  11.     // second stroked rectangle
  12.     ctx.strokeStyle = “purple”; 
  13.     ctx.lineWidth=10;
  14.     ctx.strokeRect(20, 150, 200, 100);
  15. }
  16.  
  17. // We define the 4 properties in a dedicated function, for clarity
  18. function setShadow() {
  19.     ctx.shadowColor = “Grey”;    // color
  20.     ctx.shadowBlur = 20;         // blur level
  21.     ctx.shadowOffsetX = 15;      // horizontal offset
  22.     ctx.shadowOffsetY = 15;      // vertical offset
  23. }
  • Lines 21-27: we set the 4 properties that define shadows in a dedicated function, for better clarity.
  • Line 8: we called this function once before drawing the rectangles.
  • Lines 11-18: we draw a filled and a stroked rectangle. Both rectangles cast shadows.

EXAMPLE 2: UNWANTED SHADOWS!

Let’s take a previous example: the one that draws a filled circle with an outline: http://jsbin.com/gazuba/2/edit

filled circle with outline

Now, let’s add a shadow to it, online example: http://jsbin.com/gokemu/1/edit

Here is an extract from the code:

  1. ctx.beginPath();
  2. // Add to the path a full circle (from 0 to 2PI)
  3. ctx.arc(centerX, centerY, radius, 0, 2*Math.PI, false);
  4. // With path drawing you can change the context
  5. // properties until a call to stroke() or fill() is performed
  6. ctx.fillStyle = “lightBlue”;
  7. // add shadows before drawing the filled circle
  8. addShadows();
  9. // Draws the filled circle in light blue
  10. ctx.fill();
  11. // Prepare for the outline
  12. ctx.lineWidth = 5;
  13. ctx.strokeStyle = “black”;
  14. // draws the path AGAIN (the circle), this
  15. // time in wireframe
  16. ctx.stroke();
  17. // Notice we only once called context.arc()! And drew it twice
  18. // with different styles
  19.  
  20. function addShadows(){
  21.     ctx.shadowColor = “Grey”; // color
  22.     ctx.shadowBlur = 20;      // blur level
  23.     ctx.shadowOffsetX = 15;   // horizontal offset
  24.     ctx.shadowOffsetY = 15;   // vertical offset
  25. }

And here is the result:

unwanted shadow casted by the outline

Ah, indeed, the call to ctx.fill() casts a shadow, but the call to ctx.stroke(), that paints the whole path again, casts a shadow too, and this time the outline produces an unwanted shadow… How can we avoid this effect, while using the same technique for drawing the path?

The trick is to save the context before setting the shadow properties, then draw the filled circle, then restore the context (to its previous state: without shadows), then draw the outlined circle by calling ctx.stroke().

Correct version of the code: http://jsbin.com/kedobi/2/edit

  1. // save the context before setting shadows and drawing the filled circle
  2.  ctx.save();
  3. // With path drawing you can change the context
  4. // properties until a call to stroke() or fill() is performed
  5.  ctx.fillStyle = “lightBlue”;
  6. // add shadows before drawing the filled circle
  7.  addShadows();
  8. // Draws the filled circle in light blue
  9.  ctx.fill();
  10. // restore the context to its previous saved state
  11.  ctx.restore();

And here is the final result:

unwanted shadow disappeared

Styling lines

Several context properties can be used to set the thickness of the shape outlines, the way line end caps are drawn, etc.

They apply to all shapes that are drawn in path mode (lines, curves, arcs) and some also apply to rectangles.

LINE STYLE: CHANGE THE LINE THICKNESS

We have seen this before. This is done by changing the value (in pixels) of the lineWidth property of the context:

  1. ctx.lineWidth = 10; // set the thickness of every shape drawn in stroke/wireframe mode to 10 pixels

Here is a complete example where we draw with a lineWidth of 20 pixels. You can play with the complete interactive example here: http://jsbin.com/dacuco/2/edit

line width changed

Source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>A simple example of lineWidth property use</title>
  5. </head>
  6. <body>
  7.    <canvas id=“myCanvas” width=“500”>
  8.        Your browser does not support the canvas tag.
  9.    </canvas>
  10. var canvas = document.getElementById(‘myCanvas’);
  11. var ctx = canvas.getContext(‘2d’);
  12.  
  13. // first path
  14. ctx.moveTo(20, 20);
  15. ctx.lineTo(100, 100);
  16. ctx.lineTo(100, 0);
  17.  
  18.  
  19. // second part of the path
  20. ctx.moveTo(120, 20);
  21. ctx.lineTo(200, 100);
  22. ctx.lineTo(200, 0);
  23.  
  24. // indicate stroke color + draw first part of the path
  25. ctx.strokeStyle = “#0000FF”;
  26. // Current line thickness is 20 pixels
  27. ctx.lineWidth = 20;
  28. ctx.stroke();        // draws the whole path at once
  29.  
  30. // Draws a rectangle in immediate mode
  31. ctx.strokeRect(230, 10, 100, 100);
  32.  
  33. </body>
  34. </html>

LINE STYLE: CHANGING THE END CAPS FOR A LINE

The lineCap property of the context indicates the way line end caps are rendered. Possible values are butt(default), round, square (from top to bottom in the next illustration). Note that a value of “round” or “square” makes the lines slightly longer than the default value “butt”.

line cap values

Try the next example interactively:  http://jsbin.com/yaliya/2/edit

line cap values table

Source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.    <title>A simple example of lineCap property use</title>
  5. </head>
  6. <body>
  7.  
  8. <canvas id=“myCanvas” width=“500”>
  9.     Your browser does not support the canvas tag.</canvas>
  10. var canvas = document.getElementById(‘myCanvas’);
  11. var ctx = canvas.getContext(‘2d’);
  12.  
  13. // first path
  14. ctx.moveTo(20, 20);
  15. ctx.lineTo(100, 100);
  16. ctx.lineTo(100, 30);
  17.  
  18. // second part of the path
  19. ctx.moveTo(120, 20);
  20. ctx.lineTo(200, 100);
  21. ctx.lineTo(200, 30);
  22.  
  23. // indicate stroke color + draw first part of the path
  24. ctx.strokeStyle = “#0000FF”;
  25. // Current line thickness is 20 pixels
  26. ctx.lineWidth = 20;
  27.  
  28. // Try different values: butt, square, round
  29. ctx.lineCap = “round”;
  30.  
  31. ctx.stroke();
  32. // Draws a rectangle
  33. ctx.strokeRect(230, 10, 100, 100);
  34.  
  35. </body>
  36. </html>

Note that in this example, the rectangle is not affected. It has no line ends visible – all its sides meet. However, the next property we’re going to look at will have an effect on rectangles!

LINE STYLE: SETTING THE TYPE OF CORNER WHEN TWO LINES MEET

The lineJoin property of the context indicates the way corners are rendered, when two lines meet. Possible values are miter (the default) for creating sharp corners, round, or bevel for “cut corners”.

Try the next example interactively: http://jsbin.com/dozida/2/edit

lineJoin value table

Source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>A simple example of lineJoin property use</title>
  5. </head>
  6. <body>
  7.  
  8. <canvas id=“myCanvas” width=“500”>Your browser does not support the canvas tag.</canvas>
  9.  
  10. var canvas = document.getElementById(‘myCanvas’);
  11. var ctx = canvas.getContext(‘2d’);
  12. // first path
  13. ctx.moveTo(20, 20);
  14. ctx.lineTo(100, 100);
  15. ctx.lineTo(100, 30);
  16.  
  17. // second part of the path
  18. ctx.moveTo(120, 20);
  19. ctx.lineTo(200, 100);
  20. ctx.lineTo(200, 30);
  21.  
  22. // indicate stroke color + draw first part of the path
  23. ctx.strokeStyle = “#0000FF”;
  24. // Current line thickness is 20 pixels
  25. ctx.lineWidth = 20;
  26. // Try different values : miter(default), bevel, round
  27. ctx.lineJoin = “round”;
  28. ctx.stroke();
  29. // Draws a rectangle
  30. ctx.strokeRect(230, 10, 100, 100);
  31.  
  32. </body>
  33. </html>

LINE STYLE: SPECIFIC CASE OF LINEJOIN=”MITER”, THE MITERLIMIT PROPERTY, A WAY TO AVOID LOOOOOOONG CORNERS!

The miterLimit property value corresponds to the maximum miter length: the distance between the inner corner and the outer corner where two lines meet. When the angle of a corner between two lines gets smaller, the miter length grows and can become too long.

In order to avoid this situation, we can set the miterLimit property of the context to a threshold value. If the miter length exceeds the miterLimit value, then the corner will be rendered as if the lineJoin property had been set to “bevel” and the corner will be “cut”.

miterLimit property shown with 3 different angles, we see that the part tha goes out of the angle can become very long

You can try an interactive example here: http://jsbin.com/bokusa/3/edit

In the example, try different values for the miterLimit property. You’ll see that the way the corners are rendered changes at values around 2 and 3.

Source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>A simple example of miterLimit property use</title>
  5. </head>
  6. <body>
  7.  
  8. <canvas id=“myCanvas” width=“500”>Your browser does not support the canvas tag.</canvas>
  9.  
  10. var canvas = document.getElementById(‘myCanvas’);
  11. var ctx = canvas.getContext(‘2d’);
  12. // first path
  13. ctx.moveTo(20, 20);
  14. ctx.lineTo(100, 100);
  15. ctx.lineTo(100, 30);
  16.  
  17. // second part of the path
  18. ctx.moveTo(120, 20);
  19. ctx.lineTo(200, 100);
  20. ctx.lineTo(200, 30);
  21.  
  22. // indicate stroke color + draw first part of the path
  23. ctx.strokeStyle = “#0000FF”;
  24. // Current line thickness is 20 pixels
  25. ctx.lineWidth = 20;
  26. // Try different values : miter(default), bevel, round
  27. ctx.lineJoin = “miter”;
  28. // try to change this value, try 2, 3, 4, 5 et…
  29. ctx.miterLimit = 1;
  30. ctx.stroke();
  31.  
  32. // Draws a rectangle
  33. ctx.strokeRect(230, 10, 100, 100);
  34. </body>
  35. </html>

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s