Canvas 2D Context
Part 3. Path Painting
Rewritten proverb
Stroking
Path painting operations enable the developer to define the pen and fill styles of the path contours. So far we have utilized the stroke() path painting method. To stroke means to apply a certain pen style to a path segment. The final appearance of the stroked shape depends on a combination of the stroke style and attributes exposed as members of CanvasDrawingStyles interface. The stroke style is the color, gradient or pattern of the stroking line. In the example below the stroke styles assume the form of CSS colors: the stroke style for the inner rectangle is a hexadecimal #01EF03 value, the intermediary rectangle has the stroke style determined as an RGB color function, and the stroke style of the biggest rectangle is specified by the keyword magenta:
...
context.strokeStyle="magenta";
...
context.strokeStyle="rgb (255, 0, 0)";
...
context.strokeStyle="#01EF03";
...
Members of CanvasDrawingStyles API bear information about line width, endcap style and subpath join style for geometric shapes. The lineWidth attribute uses coordinate space units to determine the thickness of the stroking line. Default value of the line width is 1.0. In the aforementioned example the width was set explicitly to 2:
context.lineWidth=2;
The rest of the CanvasDrawingStyles attributes are lineCap, lineJoin and miterLimit. The line cap style denotes the shape used at the ends of path segments. Allowed values in the string format are "butt" (default), "round" and "square". When the endcap style is set to butt, the stroke is squared off at the end of the line. The round cap makes the user agent to draw a filled semicircular arc around the end point. The diameter of the arc is equal to the line width. The square cap is similar to the butted one, but it is projecting beyond the end point of the path. A distance of projection is half the line width. In the example below all three lines have the same x values at their start and end points. However, their appearances differ, and this is the result of applying different line cap styles:
context.strokeStyle="red";
context.lineWidth=16;
context.lineCap="butt";
...
context.strokeStyle="green";
context.lineWidth=16;
context.lineCap="round";
...
context.strokeStyle="blue";
context.lineWidth=16;
context.lineCap="square";
When two segments of a path are connected the joining point is rendered according to the value of the lineJoin attribute. Permitted values are "bevel", "round" and "miter" (default). Using bevel joins makes two segments be finished with butt caps, then the notch beyond the ends of the segments are filled with the triangle formatted according to the current stroke style. Applying round joins implies drawing an arc around the meeting point of two segments. The diameter of the arc is equal to the line width. In case of using the miter join, both segments are extended until they meet at a certain angle. The subsidiary miterLimit attribute can be utilized to define the ratio of the miter length to the line width. This is a numerical value which is 10.0 by default.
context.strokeStyle="red";
context.lineWidth=16;
context.lineJoin="bevel";
...
context.strokeStyle="green";
context.lineWidth=16;
context.lineJoin="round";
...
context.strokeStyle="blue";
context.lineWidth=16;
context.lineJoin="miter";
Filling
Filling is the formatting operation painting a region enclosed by the current path. After a shape's outline is rendered according to a pen style, the interior of the shape is filled with a solid color, a gradient of one color to another or a repeating pattern. The main attribute used to fill a contour is fillStyle. The simplest scenario is the use of CSS colors. More elaborate graphic effects can be achieved by designing a gradient or creating a pattern from an external resource. Linear CanvasGradient is instantiated directly by invoking the createLinearGradient(x0, y0, x1, y1) method on the rendering context:
var linearGradient=context.createLinearGradient(0, 0, 500, 0);
linearGradient.addColorStop(0.1, "red");
linearGradient.addColorStop(0.3, "yellow");
linearGradient.addColorStop(0.5, "green");
linearGradient.addColorStop(0.7, "crimson");
linearGradient.addColorStop(0.9, "blue");
context.fillStyle=linearGradient;
context.beginPath();
context.rect(0, 0, 500, 500);
context.fill();
The linear gradient is an example of axial shading where gradient fill is distributed along a line between two points The createLinearGradient (0, 0, 500, 0) method accepts two pairs of arguments. The first pair (0, 0) defines the beginning position of the gradient. The ending position is placed at (500, 0). The virtual line connecting two points determines the direction of the gradient's changes: a line perpendicular to the direction line must have the color equivalent to the color at the point where two lines cross. Smooth transition between colors abide by the rules specified in addColorStop(offset, color) methods. The current context filling style is set to the linear gradient, then a path construction operation is performed: a rectangle with the specified upper left corner, width and height is built. The last command applies the gradient fill to the rectangle and renders it.
Radial gradients belong to the group of radial shadings distributing a color blend between two circles. This type of gradients is created by invoking the createRadialGradient(x0, y0, radius0, x1, y1, radius1) method on the CanvasRenderingContext2D instance. The start circle (gradient circle) with the radius radius0 has the center at (x0, y0). The end circle of the gradient has the origin at the focal point (x1, y1) and the radius radius1. Similar to linear gradients, color transitions depend on multiple addColorStop (offset, color) methods. In the example below the center points of both circles coincide: this is the point located at (250, 250). The inner radius is equal to 50, and the outer one is 200. The primitive to fill is an elliptical arc forming the closed circle with the radius of 200:
var radialGradient=context.createRadialGradient(250, 250, 50, 250, 250, 200);
radialGradient.addColorStop(0.1, "red");
radialGradient.addColorStop(0.3, "yellow");
radialGradient.addColorStop(0.5, "green");
radialGradient.addColorStop(0.7, "crimson");
radialGradient.addColorStop(0.9, "blue");
context.fillStyle=radialGradient;
context.arc(250, 250, 200, 0.0, 6.28);
context.fill();
In addition to shading patterns, CanvasRenderingContext2D API exposes a method allowing the developer to make use of tiling patterns. A tiling pattern consists of a graphical figure replicated along the horizontal or vertical axis of the coordinate space. The figure called pattern cell can find its source in another canvas, an HTML image, or even an HTML5 video element. Here's an example of applying a subsidiary canvas to represent pattern cells. The canvas is replicated both along the x- and y- axes. Choosing the type of the pattern cell is the responsibility of the createPattern(patternCell, repetitionStyle) method invoked on the CanvasRenderingContext2D instance. Allowed string values for repetition styles are "repeat", "repeat-x", "repeat-y" and "no-repeat".
// canvas used to get the pattern cell
var patternCellCanvas=document.getElementById("patternCellSource");
var auxiliaryContext=patternCellCanvas.getContext("2d");
var linearGradient=auxiliaryContext.createLinearGradient(2, 2, 95, 95);
linearGradient.addColorStop(0.1, "maroon");
linearGradient.addColorStop(0.9, "crimson");
auxiliaryContext.lineWidth=1;
auxiliaryContext.strokeStyle="pink";
auxiliaryContext.fillStyle=linearGradient;
auxiliaryContext.rect(2, 2, 95, 95);
auxiliaryContext.stroke();
auxiliaryContext.fill();
// canvas using the tiling pattern specified above
var targetCanvas=document.getElementById("targetSurface");
var renderingContext=targetCanvas.getContext("2d");
var pattern=renderingContext.createPattern(patternCellCanvas, "repeat");
renderingContext.fillStyle=pattern;
renderingContext.strokeStyle="pink";
renderingContext.rect(10, 10, 400, 400);
renderingContext.stroke();
renderingContext.fill();
pattern cell
Using an image or a video frame as the repeating pattern is more straightforward. It should be taken into account that the execution of the code will raise a TypeError exception if the browser does not support video frame capture. An HTMLImageElement can be located on the same page as the canvas, or created programmatically. In the example below the source of the image is defined as an external Web resource. The sampled image is replicated rightwards and downwards from the upper left corner of the rectangle:
var patternCellImage=new Image();
patternCellImage.src="http://example.com/resources/background.png";
// canvas using the tiling pattern specified above
var targetCanvas=document.getElementById("targetSurface");
var renderingContext=targetCanvas.getContext("2d");
var pattern=renderingContext.createPattern(patternCellImage, "repeat");
renderingContext.fillStyle=pattern;
renderingContext.rect(10,10, 400, 400);
renderingContext.fill();
pattern cell
Gradients and patterns can be used not only for defining the filling style of a shape, but also for stroking operations:
var linearGradient=context.createLinearGradient(0, 0, 500, 0);
linearGradient.addColorStop(0.1, "magenta");
linearGradient.addColorStop(0.3, "yellow");
linearGradient.addColorStop(0.5, "green");
linearGradient.addColorStop(0.7, "crimson");
linearGradient.addColorStop(0.9, "blue");
context.fillStyle="red";
context.lineWidth=12;
context.lineJoin="round";
context.strokeStyle=linearGradient;
context.rect(50, 50, 400, 400);
context.fill();
context.stroke();