HTML - tutorial - 37 - canvas - 01

Revision:


Content

The graphics canvas element Drawing shapes with canvas Applying styles and colors Drawing text Using images Transformations provide more powerful ways to translate the origin. Compositing and clipping


The graphics canvas element

top

<canvas> is an HTML element, which can be used to draw graphics via scripting (usually JavaScript). This can, for instance, be used to draw graphs, combine photos, or create simple animations. First introduced in WebKit by Apple for the macOS Dashboard, <canvas> has since been implemented in browsers. Today, all major browsers support it.

The default size of the canvas is 300 pixels × 150 pixels (width × height). But custom sizes can be defined using the HTML height and width property.

The <canvas> element

example:

<canvas id="tutorial" width="150" height="150"></canvas>

The <canvas> element has only two attributes: width and height. These are both optional and can also be set using DOM properties.

When no width and height attributes are specified, the canvas will initially be 300 pixels wide and 150 pixels high.
The element can be sized arbitrarily by CSS, but during rendering the image is scaled to fit its layout size: if the CSS sizing doesn't respect the ratio of the initial canvas, it will appear distorted.
If your renderings seem distorted, try specifying your width and height attributes explicitly in the <canvas> attributes, and not using CSS.

The <canvas> element must be made accessible by providing fallback text to be displayed when the media doesn't load or the user is unable to experience it as intended. You should always provide fallback content, captions, and alternative text, as appropriate for the media type. Providing fallback content is very straightforward: just insert alternate content inside the <canvas> element to be accessed by screen readers, spiders, and other automated bots. Browsers, by default, will ignore the content inside the container, rendering the canvas normally unless <canvas> isn't supported.

The <canvas> element requires the closing tag (</canvas>). If this tag is not present, the rest of the document would be considered the fallback content and wouldn't be displayed.

The <canvas> element creates a fixed-size drawing surface that exposes one or more rendering contexts, which are used to create and manipulate the content shown. The focus is here on the 2D rendering context. Other contexts may provide different types of rendering; for example, WebGL uses a 3D context based on OpenGL ES.

The canvas is initially blank. To display something, a script first needs to access the rendering context and draw on it.
The <canvas> element has a method called getContext(), used to obtain the rendering context and its drawing functions. getContext() takes one parameter, the type of context. For 2D graphics, you specify "2d" to get a CanvasRenderingContext2D.

example
code:
                    <div>
                        <canvas id="learning" width="150" height="150" onload="draw();"></canvas>
                    </div>
                    <style>
                        canvas {border: 0.1vw solid black;}
                    </style>
                    <script>
                        function draw() {
                            const canvas = document.getElementById("learning");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                            }
                        }
                    </script>
                

example

click in the square with the mouse

code:
                    <div>
                        <canvas id="canvas-1" width="150" height="150" onclick="drawIt();"></canvas>
                    </div>
                    <style>
                        #canvas-1 {border: 0.2vw solid red;}
                    </style>
                    <script>
                        function drawIt() {
                            const canvas = document.getElementById("canvas-1");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                ctx.fillStyle = "rgb(200, 0, 0)";
                                ctx.fillRect(10, 10, 50, 50);
                                ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
                                ctx.fillRect(30, 30, 50, 50);
                            }
                        }
                    </script>
                

Attributes

This element's attributes include the global attributes:

The height of the coordinate space in CSS pixels; defaults to 150.
moz-opaque: non-standard; deprecated; lets the canvas know whether translucency will be a factor; if the canvas knows there's no translucency, painting performance can be optimized; this is only supported by Mozilla-based browsers; use the standardized canvas.getContext('2d', { alpha: false }) instead.
The width of the coordinate space in CSS pixels; defaults to 300.

Usage

Alternative content: you should provide alternate content inside the <canvas> block, which will be rendered both on older browsers that don't support canvas and in browsers with JavaScript disabled.

Unlike the <img> element, the <canvas> element requires the closing tag (</canvas>).

The displayed size of the canvas can be changed using CSS, but if you do this the image is scaled during rendering to fit the styled size, which can make the final graphics rendering end up being distorted.It is better to specify your canvas dimensions by setting the width and height attributes directly on the <canvas> elements, either directly in the HTML or by using JavaScript.

The maximum size of a <canvas> element is very large, but the exact size depends on the browser:

Chrome : maximum height: 32,767 pixels; maximum width: 32,767 pixels; maximum area: 268,435,456 pixels (i.e., 16,384 x 16,384)
Firefox : maximum height: 32,767 pixels; maximum width: 32,767 pixels; maximum area: 472,907,776 pixels (i.e., 22,528 x 20,992)
Safari : maximum height: 32,767 pixels; maximum width: 32,767 pixels; maximum area: 268,435,456 pixels (i.e., 16,384 x 16,384)
IE : maximum height: 8,192 pixels; maximum width: 8,192 pixels; maximum area: ?

Note: Exceeding the maximum dimensions or area renders the canvas unusable — drawing commands will not work.


Drawing shapes with canvas

top

The origin of the canvas grid is positioned in the top left corner at coordinate (0,0). All elements are placed relative to this origin.

<canvas> only supports two primitive shapes: rectangles and paths (lists of points connected by lines). All other shapes must be created by combining one or more paths. Luckily, we have an assortment of path drawing functions which make it possible to compose very complex shapes.

Rectangle : there are three functions that draw rectangles on the canvas:

fillRect(x, y, width, height) : draws a filled rectangle.
strokeRect(x, y, width, height) : draws a rectangular outline.
clearRect(x, y, width, height) : clears the specified rectangular area, making it fully transparent.

Each of these three functions takes the same parameters. x and y specify the position on the canvas (relative to the origin) of the top-left corner of the rectangle. width and height provide the rectangle's size.

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-2" width="150" height="150" onmouseover="drawMe();"></canvas>
                    </div>
                    <style>
                        #canvas-2 {border: 0.2vw solid blue;}
                    </style>
                    <script>
                        function drawMe() {
                            const canvas = document.getElementById("canvas-2");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                ctx.fillRect(25, 25, 100, 100);
                                ctx.clearRect(45, 45, 60, 60);
                                ctx.strokeRect(50, 50, 50, 50);
                            }
                        }
                    </script>
                

Path: a path is a list of points

These points are connected by segments of lines that can be of different shapes, curved or not, of different width and of different color. A path, or even a subpath, can be closed.

To make shapes using paths, we take some extra steps:

First, create the path.
Then, use drawing commands to draw into the path.
Once the path has been created, stroke or fill the path to render it.

Functions used to perform these steps:

beginPath() : creates a new path; once created, future drawing commands are directed into the path and used to build the path up.

Path methods : methods to set different paths for objects :

closePath() : adds a straight line to the path, going to the start of the current sub-path;
stroke() : draws the shape by stroking its outline.
fill() : draws a solid shape by filling the path's content area.

First step : call the beginPath(). Internally, paths are stored as a list of sub-paths (lines, arcs, etc.) which together form a shape. Every time this method is called, the list is reset and we can start drawing new shapes. When the current path is empty, such as immediately after calling beginPath(), or on a newly created canvas, the first path construction command is always treated as a moveTo(), regardless of what it actually is. For that reason, you will almost always want to specifically set your starting position after resetting a path.

Second step: call the methods that actually specify the paths to be drawn.

Third, and an optional step: call closePath(). This method tries to close the shape by drawing a straight line from the current point to the start. If the shape has already been closed or there's only one point in the list, this function does nothing.

Note: When you call fill(), any open shapes are closed automatically, so you don't have to call closePath(). This is not the case when you call stroke().

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-3" width="150" height="150" onmouseover="drawThis();"></canvas>
                    </div>
                    <style>
                        #canvas-3 {border: 0.2vw solid orange;}
                    </style>
                    <script>
                        function drawThis() {
                            const canvas = document.getElementById("canvas-3");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                ctx.beginPath();
                                ctx.moveTo(75, 50);
                                ctx.lineTo(100, 75);
                                ctx.lineTo(100, 25);
                                ctx.fill();
                            }
                        }
                    </script>
                

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-4" width="150" height="150" onmouseover="drawFace();"></canvas>
                    </div>
                    <style>
                        #canvas-4 {border: 0.2vw solid lightgreen;}
                    </style>
                    <script>
                        function drawFace() {
                            const canvas = document.getElementById("canvas-4");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                ctx.beginPath();
                                ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // Outer circle
                                ctx.moveTo(110, 75);
                                ctx.arc(75, 75, 35, 0, Math.PI, false); // Mouth (clockwise)
                                ctx.moveTo(65, 65);
                                ctx.arc(<, 65, 5, 0, Math.PI * 2, true); // Left eye
                                ctx.moveTo(95, 65);
                                ctx.arc(90, 65, 5, 0, Math.PI * 2, true); // Right eye
                                ctx.stroke();
                            }
                        }
                        
                    </script>
                

For drawing straight lines, use the lineTo() method.

lineTo(x, y) : draws a line from the current drawing position to the position specified by x and y.

This method takes two arguments, x and y, which are the coordinates of the line's end point. The starting point is dependent on previously drawn paths, where the end point of the previous path is the starting point for the following, etc. The starting point can also be changed by using the moveTo() method.

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-5" width="150" height="150" onmouseover="drawLine();"></canvas>
                    </div>
                    <style>
                        #canvas-5 {border: 0.2vw solid burlywood;}
                    </style>
                    <script>
                        function drawLine() {
                            const canvas = document.getElementById("canvas-5");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                // Filled triangle
                                ctx.beginPath();
                                ctx.moveTo(25, 25);
                                ctx.lineTo(105, 25);
                                ctx.lineTo(25, 105);
                                ctx.fill();
            
                                // Stroked triangle
                                ctx.beginPath();
                                ctx.moveTo(125, 125);
                                ctx.lineTo(125, 45);
                                ctx.lineTo(45, 125);
                                ctx.closePath();
                                ctx.stroke();
                            }
                        }
                    </script>
                

To draw arcs or circles, use the arc() or arcTo() methods.

arc(x, y, radius, startAngle, endAngle, counterclockwise) : draws an arc which is centered at (x, y) position with radius r starting at startAngle and ending at endAngle going in the given direction indicated by counterclockwise (defaulting to clockwise).

x and y are the coordinates of the center of the circle on which the arc should be drawn.
radius is self-explanatory.
The startAngle and endAngle parameters define the start and end points of the arc in radians, along the curve of the circle. These are measured from the x axis.
The counterclockwise parameter is a Boolean value which, when true, draws the arc counterclockwise; otherwise, the arc is drawn clockwise.

arcTo(x1, y1, x2, y2, radius) : draws an arc with the given control points and radius, connected to the previous point by a straight line.

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-6" width="200" height="200" onmouseover="drawArc();"></canvas>
                    </div>
                    <style>
                        #canvas-6 {border: 0.2vw solid indigo;}
                    </style>
                    <script>
                        function drawArc() {
                            const canvas = document.getElementById("canvas-6");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                for (let i = 0; i < 4; i++) {
                                    for (let j = 0; j < 3; j++) {
                                        ctx.beginPath();
                                        const x = 25 + j * 50; // x coordinate
                                        const y = 25 + i * 50; // y coordinate
                                        const radius = 20; // Arc radius
                                        const startAngle = 0; // Starting point on circle
                                        const endAngle = Math.PI + (Math.PI * j) / 2; // End point on circle
                                        const counterclockwise = i % 2 !== 0; // clockwise or counterclockwise
            
                                        ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise);
            
                                        if (i > 1) {
                                        ctx.fill();
                                        } else {
                                        ctx.stroke();
                                        }
                                    }
                                }
                            }
                        }
                    </script>
                

Bézier curves are available in both cubic and quadratic varieties.

These are generally used to draw complex organic shapes.

quadraticCurveTo(cp1x, cp1y, x, y) : draws a quadratic Bézier curve from the current pen position to the end point specified by x and y, using the control point specified by cp1x and cp1y.

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) : draws a cubic Bézier curve from the current pen position to the end point specified by x and y, using the control points specified by (cp1x, cp1y) and (cp2x, cp2y).

The difference between these is that a quadratic Bézier curve has a start and an end point and just one control point, while a cubic Bézier curve uses two control points.

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-7" width="200" height="200" onmouseover="drawBezier();"></canvas>
                    </div>
                    <style>
                        #canvas-7 {border: 0.2vw solid violet;}
                    </style>
                    <script>
                        function drawBezier() {
                            const canvas = document.getElementById("canvas-7");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                // Quadratic curves example
                                ctx.beginPath();
                                ctx.moveTo(75, 25);
                                ctx.quadraticCurveTo(25, 25, 25, 62.5);
                                ctx.quadraticCurveTo(25, 100, 50, 100);
                                ctx.quadraticCurveTo(50, 120, 30, 125);
                                ctx.quadraticCurveTo(60, 120, 65, 100);
                                ctx.quadraticCurveTo(125, 100, 125, 62.5);
                                ctx.quadraticCurveTo(125, 25, 75, 25);
                                ctx.stroke();
                            }
                        }
                    </script>
                

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-8" width="200" height="200" onmouseover="drawBezier_2();"></canvas>
                    </div>
                    <style>
                        #canvas-8 {border: 0.2vw solid violet;}
                    </style>
                    <script>
                        function drawBezier_2() {
                            const canvas = document.getElementById("canvas-8");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                // Cubic curves example
                                ctx.beginPath();
                                ctx.moveTo(75, 40);
                                ctx.bezierCurveTo(75, 37, 70, 25, 50, 25);
                                ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
                                ctx.bezierCurveTo(20, 80, 40, 102, 75, 120);
                                ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
                                ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
                                ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
                                ctx.fill();
                            }
                        }
                    </script>
                

The rect() method adds a rectangular path to a currently open path.

rect(x, y, width, height) : draws a rectangle whose top-left corner is specified by (x, y) with the specified width and height. Before this method is executed, the moveTo() method is automatically called with the parameters (x,y). In other words, the current pen position is automatically reset to the default coordinates.

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-9" width="200" height="200" onmouseover="drawRect();"></canvas>
                    </div>
                    <style>
                        #canvas-9 {border: 0.2vw solid brown;}
                    </style>
                    <script>
                        function drawRect(){
                            const canvas = document.getElementById("canvas-9");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                ctx.rect(20,20, 150, 150);
                                ctx.fill();
                            }
                        }
                    </script>
                

There's no limitation to the number or types of paths you can use to create a shape

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-10" width="200" height="200" onmouseover="drawMany();"></canvas>
                    </div>
                    <style>
                        #canvas-10 {border: 0.4vw outset yellow;}
                    </style>
                    <script>
                        function drawMany(){
                            const canvas = document.getElementById("canvas-10");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                roundedRect(ctx, 12, 12, 150, 150, 15);
                                roundedRect(ctx, 19, 19, 150, 150, 9);
                                roundedRect(ctx, 53, 53, 49, 33, 10);
                                roundedRect(ctx, 53, 119, 49, 16, 6);
                                roundedRect(ctx, 135, 53, 49, 33, 10);
                                roundedRect(ctx, 135, 119, 25, 49, 10);
            
                                ctx.beginPath();
                                ctx.arc(37, 37, 13, Math.PI / 7, -Math.PI / 7, false);
                                ctx.lineTo(31, 37);
                                ctx.fill();
            
                                for (let i = 0; i < 8; i++) {
                                ctx.fillRect(51 + i * 16, 35, 4, 4);
                                }
            
                                for (let i = 0; i < 6; i++) {
                                    ctx.fillRect(115, 51 + i * 16, 4, 4);
                                }
            
                                for (let i = 0; i < 8; i++) {
                                    ctx.fillRect(51 + i * 16, 99, 4, 4);
                                }
            
                                ctx.beginPath();
                                ctx.moveTo(83, 116);
                                ctx.lineTo(83, 102);
                                ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
                                ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
                                ctx.lineTo(111, 116);
                                ctx.lineTo(106.333, 111.333);
                                ctx.lineTo(101.666, 116);
                                ctx.lineTo(97, 111.333);
                                ctx.lineTo(92.333, 116);
                                ctx.lineTo(87.666, 111.333);
                                ctx.lineTo(83, 116);
                                ctx.fill();
            
                                ctx.fillStyle = "white";
                                ctx.beginPath();
                                ctx.moveTo(91, 96);
                                ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
                                ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
                                ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
                                ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
                                ctx.moveTo(103, 96);
                                ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
                                ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
                                ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
                                ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
                                ctx.fill();
            
                                ctx.fillStyle = "black";
                                ctx.beginPath();
                                ctx.arc(101, 102, 2, 0, Math.PI * 2, true);
                                ctx.fill();
            
                                ctx.beginPath();
                                ctx.arc(89, 102, 2, 0, Math.PI * 2, true);
                                ctx.fill();
                            }
                        }
            
                        // A utility function to draw a rectangle with rounded corners.
                        function roundedRect(ctx, x, y, width, height, radius) {
                            ctx.beginPath();
                            ctx.moveTo(x, y + radius);
                            ctx.arcTo(x, y + height, x + radius, y + height, radius);
                            ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);
                            ctx.arcTo(x + width, y, x + width - radius, y, radius);
                            ctx.arcTo(x, y, x, y + radius, radius);
                            ctx.stroke();
                        }   
                    </script>
                

The Path2D object, available in recent versions of browsers, allows to cache or record drawing commands.

The Path2D() constructor returns a newly instantiated Path2D object, optionally with another path as an argument (creates a copy), or optionally with a string consisting of SVG path data.

Syntax:

new Path2D() ; // empty path object
new Path2D(path) ; // copy from another Path2D object
new Path2D(d) ; // path from SVG path data

All path methods like "moveTo", "rect", "arc" or "quadraticCurveTo", etc., are available on Path2D objects. The Path2D API also adds a way to combine paths using the "addPath method". This can be useful when building objects from several components.

Path2D.addPath(path [, transform]): adds a path to the current path with an optional transformation matrix.

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-11" width="200" height="200" onmouseover="drawPath();"></canvas>
                    </div>
                    <style>
                        #canvas-11 {border: 0.2vw solid red;}
                    </style>
                    <script>
                        function drawPath(){
                            const canvas = document.getElementById("canvas-11");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                const rectangle = new Path2D();
                                rectangle.rect(20, 20, 50, 50);
            
                                const circle = new Path2D();
                                circle.arc(150, 55, 25, 0, 2 * Math.PI);
            
                                ctx.stroke(rectangle);
                                ctx.fill(circle);
                            }
                        }
                    </script>
                

Another powerful feature of the new canvas Path2D API is using SVG path data to initialize paths on your canvas. This might allow you to pass around path data and re-use them in both SVG and canvas.

example : const p = new Path2D("M10 10 h 80 v 80 h -80 Z");

The path will move to point (M10 10) and then move horizontally 80 points to the right (h 80), then 80 points down (v 80), then 80 points to the left (h -80), and then back to the start (z).

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-12" width="250" height="200" onmouseover="drawPath_2();"></canvas>
                    </div>
                    <style>
                        #canvas-12 {border: 0.2vw solid white;}
                    </style>
                    <script>
                        function drawPath_2(){
                            const canvas = document.getElementById("canvas-12");
                            if (canvas.getContext) {
                                const ctx = canvas.getContext("2d");
                                let path1 = new Path2D();
                                path1.rect(10, 10, 100, 100);
                                let path2 = new Path2D(path1);
                                path2.moveTo(220, <);
                                path2.arc(170, <, 50, 0, 2 * Math.PI);
                                ctx.stroke(path2);
                            }
                        }
                    </script>
                

Applying styles and colors

top

If we want to apply colors to a shape, there are two important properties

fillStyle = color : sets the style used when filling shapes; color is a string representing a CSS <color>, a gradient object, or a pattern object; by default, the stroke and fill color are set to black (CSS color value #000000).

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-a" width="250" height="200" onmouseover="color();"></canvas>
                    </div>
                    <style>
                        #canvas-a {border: 0.2vw inset blue;}
                    </style>
                    <script>
                        function color(){
                            const ctx = document.getElementById("canvas-a").getContext("2d");
                            for (let i = 0; i < 8; i++) {
                                for (let j = 0; j < 8; j++) {
                                    ctx.fillStyle = `rgb(${Math.floor(255 - 42.5 * i)}, ${Math.floor( 255 - 42.5 * j   )}, 0)`;
                                    ctx.fillRect(j * 25, i * 25, 25, 25);
                                }
                            }
                        }
                    </script>
                

strokeStyle = color : sets the style for shapes' outlines; color is a string representing a CSS <color>, a gradient object, or a pattern object; by default, the stroke and fill color are set to black (CSS color value #000000).

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-b" width="250" height="250" onmouseover="color_2();"></canvas>
                    </div>
                    <style>
                        #canvas-b {border: 0.2vw inset green;}
                    </style>
                    <script>
                        function color_2(){
                            const ctx = document.getElementById("canvas-b").getContext("2d");
                            for (let i = 0; i < 8; i++) {
                                for (let j = 0; j < 8; j++) {
                                    ctx.strokeStyle = `rgb(0, ${Math.floor(255 - 42.5 * i)}, ${Math.floor( 255 - 42.5 * j )})`;
                                    ctx.beginPath();
                                    ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, 2 * Math.PI, true);
                                    ctx.stroke();
                                }
                            }
                        }
                    </script>
                

Draw semi-transparent (or translucent) shapes

This is done by either setting the globalAlpha property or by assigning a semi-transparent color to the stroke and/or fill style.

globalAlpha = transparencyValue : applies the specified transparency value to all future shapes drawn on the canvas. The value must be between 0.0 (fully transparent) to 1.0 (fully opaque). This value is 1.0 (fully opaque) by default.

The globalAlpha property can be useful if you want to draw a lot of shapes on the canvas with similar transparency, but otherwise it's generally more useful to set the transparency on individual shapes when setting their colors.

Because the "strokeStyle" and "fillStyle" properties accept CSS rgba color values, the following notation can be use to assign a transparent color to them: ctx.strokeStyle = "rgba(255, 0, 0, 0.5)";; ctx.fillStyle = "rgba(255, 0, 0, 0.5)";.

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-c" width="250" height="250" onmouseover="color_3();"></canvas>
                    </div>
                    <style>
                        #canvas-c {border: 0.2vw inset black;}
                    </style>
                    <script>
                        function color_3(){
                            const ctx = document.getElementById("canvas-c").getContext("2d");
                            // draw background
                            ctx.fillStyle = "#FD0";
                            ctx.fillRect(0, 0, 75, 75);
                            ctx.fillStyle = "#6C0";
                            ctx.fillRect(75, 0, 75, 75);
                            ctx.fillStyle = "#09F";
                            ctx.fillRect(0, 75, 75, 75);
                            ctx.fillStyle = "#F30";
                            ctx.fillRect(75, 75, 75, 75);
                            ctx.fillStyle = "#FFF";
            
                            // set transparency value
                            ctx.globalAlpha = 0.2;
            
                            // Draw semi transparent circles
                            for (let i = 0; i < 7; i++) {
                                ctx.beginPath();
                                ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true);
                                ctx.fill();
                            }                
                        }
                    </script>
                

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-d" width="250" height="250" onmouseover="color_4();"></canvas>
                    </div>
                    <style>
                        #canvas-d {border: 0.2vw inset aqua;}
                    </style>
                    <script>
                        function color_4(){
                            const ctx = document.getElementById("canvas-d").getContext("2d");
                            // Draw background
                            ctx.fillStyle = "rgb(255, 221, 0)";
                            ctx.fillRect(0, 0, 150, 37.5);
                            ctx.fillStyle = "rgb(102, 204, 0)";
                            ctx.fillRect(0, 37.5, 150, 37.5);
                            ctx.fillStyle = "rgb(0, 153, 255)";
                            ctx.fillRect(0, 75, 150, 37.5);
                            ctx.fillStyle = "rgb(255, 51, 0)";
                            ctx.fillRect(0, 112.5, 150, 37.5);
            
                            // Draw semi transparent rectangles
                            for (let i = 0; i < 10; i++) {
                                ctx.fillStyle = `rgba(255, 255, 255, ${(i + 1) / 10})`;
                                for (let j = 0; j < 4; j++) {
                                ctx.fillRect(5 + i * 14, 5 + j * 37.5, 14, 27.5);
                                }
                            }
                        }
                    </script>
                

Line styles

There are several properties to style lines.

lineWidth = value : sets the width of lines drawn in the future. Values must be positive numbers. By default this value is set to 1.0 units.

The line width is the thickness of the stroke centered on the given path. In other words, the area that's drawn extends to half the line width on either side of the path. Because canvas coordinates do not directly reference pixels, special care must be taken to obtain crisp horizontal and vertical lines.

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-e" width="150" height="150" onmouseover="lineStyle();"></canvas>
                    </div>
                    <style>
                        #canvas-e {border: 0.2vw inset aquamarine;}
                    </style>
                    <script>
                        function lineStyle(){
                            const ctx = document.getElementById("canvas-e").getContext("2d");
                            for (let i = 0; i < 10; i++) {
                                ctx.lineWidth = 1 + i;
                                ctx.beginPath();
                                ctx.moveTo(5 + i * 14, 5);
                                ctx.lineTo(5 + i * 14, 140);
                                ctx.stroke();
                            }
                        }
                    </script>
                

lineCap = type : sets the appearance of the ends of lines. There are three possible values for this property: butt, round and square. By default this property is set to butt.

butt : the ends of lines are squared off at the endpoints.
round : the ends of lines are rounded.
square : the ends of lines are squared off by adding a box with an equal width and half the height of the line's thickness.

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-f" width="200" height="150" onmouseover="lineStyle_1();"></canvas>
                    </div>
                    <style>
                        #canvas-f {border: 0.2vw inset skyblue;}
                    </style>
                    <script>
                        function lineStyle_1(){
                            const ctx = document.getElementById("canvas-f").getContext("2d");
                            // Draw guides
                            ctx.strokeStyle = "#09f";
                            ctx.beginPath();
                            ctx.moveTo(10, 10);
                            ctx.lineTo(140, 10);
                            ctx.moveTo(10, 140);
                            ctx.lineTo(140, 140);
                            ctx.stroke();
                            // Draw lines
                            ctx.strokeStyle = "red";
                            ["butt", "round", "square"].forEach((lineCap, i) => {
                                ctx.lineWidth = 15;
                                ctx.lineCap = lineCap;
                                ctx.beginPath();
                                ctx.moveTo(25 + i * 50, 10);
                                ctx.lineTo(25 + i * 50, 140);
                                ctx.stroke();
                            });
                        }
                    </script>
                

lineJoin = type : sets the appearance of the "corners" where lines meet. There are three possible values for this property: round, bevel and miter. By default this property is set to miter.

round : rounds off the corners of a shape by filling an additional sector of disc centered at the common endpoint of connected segments. The radius for these rounded corners is equal to half the line width.
bevel : fills an additional triangular area between the common endpoint of connected segments, and the separate outside rectangular corners of each segment.
miter : connected segments are joined by extending their outside edges to connect at a single point, with the effect of filling an additional lozenge-shaped area. This setting is effected by the "miterLimit property".

Note that the lineJoin setting has no effect if the two connected segments have the same direction, because no joining area will be added in this case.

example

move mouse over the canvas

code:
                    <div>
                        <canvas id="canvas-g" width="200" height="150" onmouseover="lineStyle_2();"></canvas>
                    </div>
                    <style>
                        #canvas-g {border: 0.2vw inset lightblue;}
                    </style>
                    <script>
                        function lineStyle_2(){
                            const ctx = document.getElementById("canvas-g").getContext("2d");
                            ctx.lineWidth = 10;
                            ["round", "bevel", "miter"].forEach((lineJoin, i) => {
                                ctx.lineJoin = lineJoin;
                                ctx.beginPath();
                                ctx.moveTo(-5, 5 + i * 40);
                                ctx.lineTo(35, 45 + i * 40);
                                ctx.lineTo(75, 5 + i * 40);
                                ctx.lineTo(115, 45 + i * 40);
                                ctx.lineTo(155, 5 + i * 40);
                                ctx.stroke();
                                ctx.strokeStyle = "red"
                            });
                        }
                    </script>
                    
                

miterLimit = value : establishes a limit on the miter when two lines join at a sharp angle, to let you control how thick the junction becomes.

The miterLimit property determines how far the outside connection point can be placed from the inside connection point. If two lines exceed this value, a bevel join gets drawn instead.

Note that the maximum miter length is the product of the line width measured in the current coordinate system, by the value of this miterLimit property (whose default value is 10.0 in the HTML <canvas>), so the miterLimit can be set independently from the current display scale or any affine transforms of paths: it only influences the effectively rendered shape of line edges.

More exactly, the miter limit is the maximum allowed ratio of the extension length (in the HTML canvas, it is measured between the outside corner of the joined edges of the line and the common endpoint of connecting segments specified in the path) to half the line width. It can equivalently be defined as the maximum allowed ratio of the distance between the inside and outside points of junction of edges, to the total line width. It is then equal to the cosecant of half the minimum inner angle of connecting segments below which no miter join will be rendered, but only a bevel join: miterLimit = max miterLength / lineWidth = 1 / sin ( min θ / 2 )

example

change the "miterLimit" by entering a new value below
and clicking the redraw button.



code:
                    <div>
                        <p style="margin-left: -1vw">change the "miterLimit" by entering a new value below 
and clicking the redraw button.</p> <canvas id="canvas-h" width="150" height="100" ></canvas><br> <input type="number" id="miterLimit"><br> <button onclick="lineStyle_3()">redraw</button> </div> <style> #canvas-h {border: 0.2vw inset blue;} </style> <script> function lineStyle_3(){ const ctx = document.getElementById("canvas-h").getContext("2d"); // Clear canvas ctx.clearRect(0, 0, 150, 150); // Draw guides ctx.strokeStyle = "#09f"; ctx.lineWidth = 2; ctx.strokeRect(-5, 50, 160;, 50); // Set line styles ctx.strokeStyle = "#000"; ctx.lineWidth = 10; // check input if (document.getElementById("miterLimit").value.match(/\d+(\.\d+)?/)) { ctx.miterLimit = parseFloat(document.getElementById("miterLimit").value); } else { alert("Value must be a positive number"); } // Draw lines ctx.beginPath(); ctx.moveTo(0, 100); for (let i = 0; i < 24; i++) { const dy = i % 2 === 0 ? 25 : -25; ctx.lineTo(Math.pow(i, 1.5) * 2, 75 + dy); } ctx.stroke(); return false; } </script>

getLineDash() : returns the current line dash pattern array containing an even number of non-negative numbers.

setLineDash(segments) : sets the current line dash pattern. The method accepts a list of numbers that specifies distances to alternately draw a line and a gap. and the "lineDashOffset" property sets an offset where to start the pattern.

lineDashOffset = value : specifies where to start a dash array on a line. It sets an offset where to start the pattern.

example
code:
                    <div>
                        <canvas id="canvas-i" width="200" height="150" ></canvas>
                    
                    </div>
                    <style>
                        #canvas-i {border: 0.2vw inset aquamarine;} 
                    </style>
                    <script>
                        const ctx_1 = document.getElementById("canvas-i").getContext("2d");
                        let offset = 0;
                        let canvas = document.getElementById("canvas-i");
                        function lineStyle_5(){
                            ctx_1.clearRect(0, 0, canvas.width, canvas.height);
                            ctx_1.setLineDash([4, 2]);
                            ctx_1.lineDashOffset = -offset;
                            ctx_1.strokeRect(10, 10, 100, 100);
                        }
                        function march() {
                            offset++;
                            if (offset > 16) {
                                offset = 0;
                            }
                        lineStyle_5();
                            setTimeout(march, 20);
                        }
                        march();
                    </script>
                

A CanvasGradient object is created by using one of the following methods

This object can be assigned to the "fillStyle" or "strokeStyle" properties.

createLinearGradient(x1, y1, x2, y2) : creates a linear gradient object with a starting point of (x1, y1) and an end point of (x2, y2).

createRadialGradient(x1, y1, r1, x2, y2, r2) : creates a radial gradient. The parameters represent two circles, one with its center at (x1, y1) and a radius of r1, and the other with its center at (x2, y2) with a radius of r2.

createConicGradient(angle, x, y) : creates a conic gradient object with a starting angle of angle in radians, at the position (x, y).

Syntax examples : const lineargradient = ctx.createLinearGradient(0, 0, 150, 150);; const radialgradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 100);

Once a CanvasGradient object is created, colors can be assigned to it by using the addColorStop() method. gradient.addColorStop(position, color) : this creates a new color stop on the gradient object; the position is a number between 0.0 and 1.0 and defines the relative position of the color in the gradient.

The color argument must be a string representing a CSS <color>, indicating the color the gradient should reach at that offset into the transition.
As many color stops to a gradient can be added as needed.

example: createLinearGradient
code:
                    <div>
                        <canvas id="canvas-j" width="200" height="150" ></canvas>
                    </div>
                    <style>
                          #canvas-j {border: 0.2vw inset orangered;} 
                    </style>
                    <script>
                        function createGradient() {
                            const ctx = document.getElementById("canvas-j").getContext("2d");
        
                            // Create gradients
                            const lingrad = ctx.createLinearGradient(0, 0, 0, 150);
                            lingrad.addColorStop(0, "#00ABEB");
                            lingrad.addColorStop(0.5, "#fff");
                            lingrad.addColorStop(0.5, "#26C000");
                            lingrad.addColorStop(1, "#fff");
        
                            const lingrad2 = ctx.createLinearGradient(0, 50, 0, 95);
                            lingrad2.addColorStop(0.5, "#000");
                            lingrad2.addColorStop(1, "rgba(0, 0, 0, 0)");
        
                            // assign gradients to fill and stroke styles
                            ctx.fillStyle = lingrad;
                            ctx.strokeStyle = lingrad2;
        
                            // draw shapes
                            ctx.fillRect(10, 10, 180, 130);
                            ctx.strokeRect(50, 50, 100, 50);
                        }
                        createGradient();
                    </script>
                

example: createRadialGradient
code:
                    <div>
                        <canvas id="canvas-k" width="200" height="150" ></canvas>
                    </div>
                    <style>
                          #canvas-k {border: 0.2vw inset magenta;} 
                    </style>
                    <script>
                        function createGradient_2() {
                            const ctx = document.getElementById("canvas-k").getContext("2d");
                            // Create gradients
                            const radgrad = ctx.createRadialGradient(45, 45, 10, 52, 50, 30);
                            radgrad.addColorStop(0, "#A7D30C");
                            radgrad.addColorStop(0.9, "#019F62");
                            radgrad.addColorStop(1, "rgba(1, 159, 98, 0)");
        
                            const radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
                            radgrad2.addColorStop(0, "#FF5F98");
                            radgrad2.addColorStop(0.75, "#FF0188");
                            radgrad2.addColorStop(1, "rgba(255, 1, 136, 0)");
        
                            const radgrad3 = ctx.createRadialGradient(95, 15, 15, 102, 20, 40);
                            radgrad3.addColorStop(0, "#00C9FF");
                            radgrad3.addColorStop(0.8, "#00B5E2");
                            radgrad3.addColorStop(1, "rgba(0, 201, 255, 0)");
        
                            const radgrad4 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90);
                            radgrad4.addColorStop(0, "#F4F201");
                            radgrad4.addColorStop(0.8, "#E4C700");
                            radgrad4.addColorStop(1, "rgba(228, 199, 0, 0)");
        
                            // draw shapes
                            ctx.fillStyle = radgrad4;
                            ctx.fillRect(0, 0, 190, 150);
                            ctx.fillStyle = radgrad3;
                            ctx.fillRect(0, 0, 190, 150);
                            ctx.fillStyle = radgrad2;
                            ctx.fillRect(0, 0, 190, 150);
                            ctx.fillStyle = radgrad;
                            ctx.fillRect(0, 0, 190, 150);
                        }
                        createGradient_2();
                    </script>
                

example: createConicGradient
code:
                    <div>
                        <canvas id="canvas-l" width="300" height="150" ></canvas>
                    </div>
                    <style>
                          #canvas-l {border: 0.2vw inset steelblue;} 
                    </style>
                    <script>
                        function createGradient_3() {
                            const ctx = document.getElementById("canvas-l").getContext("2d");
                            // Create gradients
                            const conicGrad1 = ctx.createConicGradient(2, 62, 75);
                            conicGrad1.addColorStop(0, "#A7D30C");
                            conicGrad1.addColorStop(1, "#fff");
        
                            const conicGrad2 = ctx.createConicGradient(0, 187, 75);
                            // we multiply our values by Math.PI/180 to convert degrees to radians
                            conicGrad2.addColorStop(0, "black");
                            conicGrad2.addColorStop(0.25, "black");
                            conicGrad2.addColorStop(0.25, "white");
                            conicGrad2.addColorStop(0.5, "white");
                            conicGrad2.addColorStop(0.5, "black");
                            conicGrad2.addColorStop(0.75, "black");
                            conicGrad2.addColorStop(0.75, "white");
                            conicGrad2.addColorStop(1, "white");
        
                            // draw shapes
                            ctx.fillStyle = conicGrad1;
                            ctx.fillRect(12, 25, 100, 100);
                            ctx.fillStyle = conicGrad2;
                            ctx.fillRect(137, 25, 100, 100);
                        }
                        createGradient_3();
                    </script>
                

The createPattern() method.

createPattern(image, type) : creates and returns a new canvas pattern object; image is the source of the image (that is, an HTMLImageElement, a SVGImageElement, another HTMLCanvasElement or a OffscreenCanvas, an HTMLVideoElement or a VideoFrame, or an ImageBitmap); type is a string indicating how to use the image.

The type specifies how to use the image in order to create the pattern, and must be one of the following string values:

repeat : tiles the image in both vertical and horizontal directions.
repeat-x : tiles the image horizontally but not vertically.
repeat-y : tiles the image vertically but not horizontally.
no-repeat : doesn't tile the image. It's used only once.

This method is used to create a CanvasPattern object, which can be assigned to the "fillStyle" or "strokeStyle" properties.

example: createPattern
code:
                    <div>
                        <canvas id="canvas-m" width="250" height="150" ></canvas>
                    </div>
                    <style>
                            #canvas-m {border: 0.2vw inset seagreen} 
                    </style>
                    <script>
                        function createPattern(){
                            const ctx = document.getElementById("canvas-m").getContext("2d");
                                // create new image object to use as pattern
                            const img = new Image();
                            img.src = "search.png";
                            img.onload = () => {
                                // create pattern
                                const ptrn = ctx.createPattern(img, "repeat");
                                ctx.fillStyle = ptrn;
                                ctx.fillRect(0, 0, 250, 150);
                            };
                        }
                        createPattern();
                    </script>
                

Using shadows involves just four properties

shadowOffsetX = float : indicates the horizontal distance the shadow should extend from the object. This value isn't affected by the transformation matrix. The default is 0.

shadowOffsetY = float : indicates the vertical distance the shadow should extend from the object. This value isn't affected by the transformation matrix. The default is 0.

shadowBlur = float : indicates the size of the blurring effect; this value doesn't correspond to a number of pixels and is not affected by the current transformation matrix. The default value is 0.

shadowColor = color : a standard CSS color value indicating the color of the shadow effect; by default, it is fully-transparent black

The properties shadowOffsetX and shadowOffsetY indicate how far the shadow should extend from the object in the X and Y directions; these values aren't affected by the current transformation matrix. Use negative values to cause the shadow to extend up or to the left, and positive values to cause the shadow to extend down or to the right. These are both 0 by default.
The shadowBlur property indicates the size of the blurring effect; this value doesn't correspond to a number of pixels and is not affected by the current transformation matrix. The default value is 0.
The shadowColor property is a standard CSS color value indicating the color of the shadow effect; by default, it is fully-transparent black.

example: shadowed text
Sample string
code:
                    <div>
                        <canvas id="canvas-n" width="250" height="150" >Sample string</canvas>
                    </div>
                    <style>
                            #canvas-n {border: 0.2vw inset burlywood} 
                    </style>
                    <script>
                        function createShadow(){
                            const ctx = document.getElementById("canvas-n").getContext("2d");
                            ctx.shadowOffsetX = 2;
                            ctx.shadowOffsetY = 2;
                            ctx.shadowBlur = 2;
                            ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
        
                            ctx.font = "20px Times New Roman";
                            ctx.fillStyle = "Black";
                            ctx.fillText("Sample String", 5, 30);
                        }
                        createShadow();
                    </script>
                

Canvas fill rules

When using fill (or clip and isPointInPath) you can optionally provide a fill rule algorithm by which to determine if a point is inside or outside a path and thus if it gets filled or not. This is useful when a path intersects itself or is nested. Two values are possible:

nonzero : the non-zero winding rule, which is the default rule.
evenodd : the even-odd winding rule.

example: even-odd winding ruleshadowed text
code:
                    <div>
                        <canvas id="canvas-o" width="250" height="150" ></canvas>
                    </div>
                    <style>
                            #canvas-o {border: 0.2vw inset burlywood} 
                    </style>
                    <script>
                        function createRule(){
                            const ctx = document.getElementById("canvas-o").getContext("2d");
                            ctx.beginPath();
                            ctx.arc(50, 50, 30, 0, Math.PI * 2, true);
                            ctx.arc(50, 50, 15, 0, Math.PI * 2, true);
                            ctx.fill("evenodd");
                        }
                        createRule();
                    </script>
                

Drawing text

top

The canvas rendering context provides two methods to render text

fillText(text, x, y [, maxWidth]) : fills a given text at the given (x,y) position. Optionally with a maximum width to draw.

strokeText(text, x, y [, maxWidth]) : strokes a given text at the given (x,y) position. Optionally with a maximum width to draw.

example:
code:
                    <div>
                        <canvas id="canvas-1a" width="300" height="150" ></canvas>
                    </div>
                    <style>
                          #canvas-1a {border: 0.2vw inset lightgreen} 
                    </style>
                    <script>
                        function drawText(){
                            const ctx = document.getElementById("canvas-1a").getContext("2d");
                            ctx.font = "48px serif";
                            ctx.fillText("Hello world", 10, 50);
                            
                        }
                        drawText();
                        function drawText_1(){
                            const ctx = document.getElementById("canvas-1a").getContext("2d");
                            ctx.font = "48px serif";
                            ctx.strokeText("Hello world", 10, 100);
                            
                        }
                        drawText_1();
                    </script>
                

Stylng text

font = value : the current text style being used when drawing text. This string uses the same syntax as the CSS font property. The default font is 10px sans-serif.

textAlign = value : text alignment setting. Possible values: start, end, left, right or center. The default value is start.

textBaseline = value : baseline alignment setting. Possible values: top, hanging, middle, alphabetic, ideographic, bottom. The default value is alphabetic.

direction = value : directionality. Possible values: ltr, rtl, inherit. The default value is inherit.

example
code:
                    <div>
                        <canvas id="canvas-1b" width="300" height="150" ></canvas>
                    </div>
                    <style>
                        #canvas-1b {border: 0.2vw inset green} 
                    </style>
                    <script>
                        function drawText_2(){
                            const ctx = document.getElementById("canvas-1b").getContext("2d");
                            ctx.font = "48px serif";
                            ctx.textBaseline = "hanging";
                            ctx.strokeText("Hello world", 0, 50);
                            
                        }
                        drawText_2();
                        function drawText_3(){
                            const ctx = document.getElementById("canvas-1b").getContext("2d");
                            ctx.font = "48px serif";
                            ctx.textBaseline = "alphabetics"
                            ctx.strokeText("Hello world", 10, 110);
                            
                        }
                        drawText_3();
                    </script>
                

Using images

top

Images can be used to do dynamic photo compositing or as backdrops of graphs, for sprites in games, and so forth. External images can be used in any format supported by the browser, such as PNG, GIF, or JPEG. You can even use the image produced by other canvas elements on the same page as the source!

Importing images into a canvas is basically a two step process: 1/ get a reference to an HTMLImageElement object or to another canvas element as a source. It is also possible to use images by providing a URL. 2/ draw the image on the canvas using the drawImage() function.

Getting images to draw

The canvas API is able to use any of the following data types as an image source:

HTMLImageElement : these are images created using the Image() constructor, as well as any <img> element.
SVGImageElement : these are images embedded using the <image> element.
HTMLVideoElement : using an HTML <video> element as the image source grabs the current frame from the video and uses it as an image.
HTMLCanvasElement : another <canvas> element can be used as the image source.
ImageBitmap : a bitmap image, eventually cropped. Such types are used to extract part of an image, a sprite, from a larger image.
OffscreenCanvas : a special kind of <canvas> that is not displayed and is prepared without being displayed. Using such an image source allows to switch to it without the composition of the content to be visible to the user.
VideoFrame : an image representing one single frame of a video.

There are several ways to get images for use on a canvas.

Using images from the same page: a reference to images on the same page as the canvas can be obtained by using the document.images collection, the document.getElementsByTagName() method, or, if you know the ID of the specific image you wish to use, the document.getElementById() to retrieve that specific image.

Using images from other domains : using the crossorigin attribute of an <img> element (reflected by the HTMLImageElement.crossOrigin property), you can request permission to load an image from another domain for use in your call to drawImage(). If the hosting domain permits cross-domain access to the image, the image can be used in your canvas without tainting it; otherwise using the image will taint the canvas.

Using other canvas elements: other canvas elements can be accessed using either the document.getElementsByTagName() or document.getElementById() method. Be sure you've drawn something to the source canvas before using it in your target canvas.

Creating an image from scratch : another option is to create new HTMLImageElement objects in the script. To do this, you can use the convenient Image() constructor:

            const img = new Image(); // Create new img element
            img.src = "myImage.png"; // Set source path
        

When this script gets executed, the image starts loading. If you try to call drawImage() before the image has finished loading, it won't do anything (or, in older browsers, may even throw an exception). So you need to be sure to use the load event so you don't try this before the image has loaded.

Embedding an image via data:URL : another possible way to include images is via the data: URL. Data URLs allow you to completely define an image as a Base64 encoded string of characters directly in your code. One advantage of data URLs is that the resulting image is available immediately without another round trip to the server. Another potential advantage is that it is also possible to encapsulate in one file all of your CSS, JavaScript, HTML, and images, making it more portable to other locations. Some disadvantages of this method are that your image is not cached, and for larger images the encoded URL can become quite long.

Using frames from a video : frames from a video being presented by a <video> element (even if the video is not visible) can be used.

Drawing images

Once we have a reference to our source image object we can use the drawImage() method to render it to the canvas.

The drawImage() method is overloaded and has several variants, but in its most basic form it looks like this:

drawImage(image, x, y) : draws the image specified by the image parameter at the coordinates (x, y).

Note: SVG images must specify a width and height in the root <svg> element.

example
code:
                    <div>
                        <canvas id="canvas-1c" width="300" height="250" ></canvas>
                    </div>
                    <style>
                        #canvas-1c {border: 0.2vw inset darkolivegreen} 
                    </style>
                    <script>
                        function drawImage() {
                            const ctx = document.getElementById("canvas-1c").getContext("2d");
                            const img = new Image();
                            img.onload = () => {
                                ctx.drawImage(img, 0, 80);
                                ctx.beginPath();
                                ctx.moveTo(30, 96);
                                ctx.lineTo(70, 66);
                                ctx.lineTo(103, 76);
                                ctx.lineTo(170, 15);
                                ctx.stroke();
                            };
                            img.src = "search.png";
                            }
                        drawImage();
                    </script>
                

The second variant of the drawImage() method adds two new parameters and lets us place scaled images on the canvas.

drawImage(image, x, y, width, height) : this variant adds the width and height parameters, which indicate the size to which to scale the image when drawing it onto the canvas.

example
code:
                    <div>
                        <canvas id="canvas-1d" width="300" height="250" ></canvas>
                    </div>
                    <style>
                        #canvas-1d {border: 0.2vw inset olive} 
                    </style>
                    <script>
                        function drawImage_2() {
                            const ctx = document.getElementById("canvas-1d").getContext("2d");
                            const img = new Image();
                            img.onload = () => {
                                for (let i = 0; i < 6; i++) {
                                    for (let j = 0; j < 5; j++) {
                                        ctx.drawImage(img, j * 50, i * 38, 50, 38);
                                    }
                                }
                            };
                            img.src = "zoom-in.png";
                        }
                        drawImage_2();
                    </script>
                

The third and last variant of the drawImage() method has eight parameters in addition to the image source. It lets us cut out a section of the source image, then scale and draw it on our canvas.

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) : given an image, this function takes the area of the source image specified by the rectangle whose top-left corner is (sx, sy) and whose width and height are sWidth and sHeight and draws it into the canvas, placing it on the canvas at (dx, dy) and scaling it to the size specified by dWidth and dHeight.

The first four parameters define the location and size of the slice on the source image. The last four parameters define the rectangle into which to draw the image on the destination canvas.


Transformations provide more powerful ways to translate the origin.

top

Saving and restoring state

Two methods are indispensable once you start generating more complex drawings:

save() : saves the entire state of the canvas.
restore() : restores the most recently saved canvas state.

Canvas states are stored on a stack. Every time the save() method is called, the current drawing state is pushed onto the stack. A drawing state consists of:

The transformations that have been applied (i.e. translate, rotate and scale).
The current values of the following attributes: strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, lineDashOffset, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation, font, textAlign, textBaseline, direction, imageSmoothingEnabled, the current clipping path.

You can call the save() method as many times as you like. Each time the restore() method is called, the last saved state is popped off the stack and all saved settings are restored.

Translating

The translate() method is used to move the canvas and its origin to a different point in the grid. translate(x, y) moves the canvas and its origin on the grid. x indicates the horizontal distance to move, and y indicates how far to move the grid vertically.

example
code:
                    <div>
                        <canvas id="canvas-1e" width="300" height="250" ></canvas>
                    </div>
                    <style>
                        #canvas-1e {border: 0.2vw inset azure} 
                    </style>
                    <script>
                        function move() {
                            const ctx = document.getElementById("canvas-1e").getContext("2d");
                            for (let i = 0; i < 3; i++) {
                                for (let j = 0; j < 3; j++) {
                                ctx.save();
                                ctx.fillStyle = `rgb(${51 * i}, ${255 - 51 * i}, 255)`;
                                ctx.translate(10 + j * 50, 10 + i * 50);
                                ctx.fillRect(0, 0, 25, 25);
                                ctx.restore();
                                }
                            }
                        }
                        move();
                    </script>
                

Rotating

The rotate() method is used to rotate the canvas around the current origin. rotate(angle) rotates the canvas clockwise around the current origin by the angle number of radians. The rotation center point is always the canvas origin. To change the center point, the canvas has to be moved by using the translate() method.

example
code:
                    <div>
                        <canvas id="canvas-1f" width="350" height="250" ></canvas>
                    </div>
                    <style>
                        #canvas-1f {border: 0.2vw inset chartreuse} 
                    </style>
                    <script>
                        function moveRotate() {
                            const ctx = document.getElementById("canvas-1f").getContext("2d");
                            // left rectangles, rotate from canvas origin
                            ctx.save();
                            // blue rect
                            ctx.fillStyle = "#0095DD";
                            ctx.fillRect(30, 30, 100, 100);
                            ctx.rotate((Math.PI / 180) * 25);
                            // grey rect
                            ctx.fillStyle = "#4D4E53";
                            ctx.fillRect(30, 30, 100, 100);
                            ctx.restore();
                            // right rectangles, rotate from rectangle center
                            // draw blue rect
                            ctx.fillStyle = "#0095DD";
                            ctx.fillRect(150, 30, 100, 100);
                            ctx.translate(200, 80); // translate to rectangle center
                            // x = x + 0.5 * width
                            // y = y + 0.5 * height
                            ctx.rotate((Math.PI / 180) * 25); // rotate
                            ctx.translate(-200, -80); // translate back
                            // draw grey rect
                            ctx.fillStyle = "#4D4E53";
                            ctx.fillRect(150, 30, 100, 100);
                        }
                        moveRotate();
                    </script>
                

Scaling

The scaling() method is used to increase or decrease the units in our canvas grid. This can be used to draw scaled down or enlarged shapes and bitmaps. scale(x, y) scales the canvas units by x horizontally and by y vertically. Both parameters are real numbers. Values that are smaller than 1.0 reduce the unit size and values above 1.0 increase the unit size. Values of 1.0 leave the units the same size.

Using negative numbers you can do axis mirroring (for example using translate(0,canvas.height); scale(1,-1); you will have the well-known Cartesian coordinate system, with the origin in the bottom left corner).

By default, one unit on the canvas is exactly one pixel. If we apply, for instance, a scaling factor of 0.5, the resulting unit would become 0.5 pixels and so shapes would be drawn at half size. In a similar way setting the scaling factor to 2.0 would increase the unit size and one unit now becomes two pixels. This results in shapes being drawn twice as large.

example
code:
                    <div>
                        <canvas id="canvas-1g" width="350" height="250" ></canvas>
                    </div>
                    <style>
                        #canvas-1g {border: 0.2vw inset chartreuse} 
                    </style>
                    <script>
                        function moveScale() {
                            const ctx = document.getElementById("canvas-1g").getContext("2d");
                            // draw a simple rectangle, but scale it.
                            ctx.save();
                            ctx.scale(10, 3);
                            ctx.fillRect(1, 10, 10, 10);
                            ctx.restore();
                            // mirror horizontally
                            ctx.scale(-1, 1);
                            ctx.font = "48px serif";
                            ctx.fillText("MDN", -135, 120);
                        }
                        moveScale();
                    </script>
                

Transforms

The following transformation methods allow modifications directly to the transformation matrix.

transform(a, b, c, d, e, f) multiplies the current transformation matrix with the matrix described by its arguments. The transformation matrix is described by:

If any of the arguments are "Infinity" the transformation matrix must be marked as infinite instead of the method throwing an exception.

The parameters of this function are:

a (m11) : horizontal scaling.
b (m12) : horizontal skewing.
c (m21) : vertical skewing.
d (m22) : vertical scaling.
e (dx) : horizontal moving.
f (dy) : vertical moving.

setTransform(a, b, c, d, e, f) : resets the current transform to the identity matrix, and then invokes the transform() method with the same arguments. This basically undoes the current transformation, then sets the specified transform, all in one step.

resetTransform() : resets the current transform to the identity matrix. This is the same as calling: ctx.setTransform(1, 0, 0, 1, 0, 0);

example
code:
                    <div>
                        <canvas id="canvas-1h" width="350" height="250" ></canvas>
                    </div>
                    <style>
                        #canvas-1h {border: 0.2vw inset lawngreen} 
                    </style>
                    <script>
                        function moveTransform() {
                            const ctx = document.getElementById("canvas-1h").getContext("2d");
                            const sin = Math.sin(Math.PI / 6);
                            const cos = Math.cos(Math.PI / 6);
                            ctx.translate(100, 100);
                            let c = 0;
                            for (let i = 0; i <= 12; i++) {
                                c = Math.floor((255 / 12) * i);
                                ctx.fillStyle = `rgb(${c}, ${c}, ${c})`;
                                ctx.fillRect(0, 0, 100, 10);
                                ctx.transform(cos, sin, -sin, cos, 0, 0);
                            }
                            ctx.setTransform(-1, 0, 0, 1, 100, 100);
                            ctx.fillStyle = "rgba(255, 128, 255, 0.5)";
                            ctx.fillRect(0, 50, 100, 100);
                        }
                        moveTransform();
                    </script>
                

Compositing and clipping

top

globalCompositeOperation

New shapes can also be used to mask off certain areas, clear sections from the canvas (not limited to rectangles like the clearRect() method does) and more.

globalCompositeOperation = type : this sets the type of compositing operation to apply when drawing new shapes, where type is a string identifying which of the twelve compositing operations to use.

Clipping paths

A clipping path is like a normal canvas shape but it acts as a mask to hide unwanted parts of shapes.

Next to the stroke() and fill() methods, there's a third method we can use with paths, called clip().

clip() turns the path currently being built into the current clipping path. clip() is being used instead of "closePath()"" to close a path and turn it into a clipping path instead of "stroking" or "filling" the path. By default the <canvas> element has a clipping path that's the exact same size as the canvas itself. In other words, no clipping occurs.

example
code:
                    <div>
                        <canvas id="canvas-2a" width="350" height="250" ></canvas>
                    </div>
                    <style>
                        #canvas-2a {border: 0.2vw inset lightsalmon} 
                    </style>
                    <script>
                        function makeClip() {
                            const ctx = document.getElementById("canvas-2a").getContext("2d");
                            ctx.fillRect(0, 0, 250, 250);
                            ctx.translate(95, 95);
                            // Create a circular clipping path
                            ctx.beginPath();
                            ctx.arc(0, 0, 80, 0, Math.PI * 2, true);
                            ctx.clip();
                            // draw background
                            const lingrad = ctx.createLinearGradient(0, -65, 0, 65);
                            lingrad.addColorStop(0, "#232256");
                            lingrad.addColorStop(1, "#143778");
                            ctx.fillStyle = lingrad;
                            ctx.fillRect(-75, -75, 150, 150);
                            // draw stars
                            for (let j = 1; j < 50; j++) {
                                ctx.save();
                                ctx.fillStyle = "#fff";
                                ctx.translate(
                                75 - Math.floor(Math.random() * 150),
                                75 - Math.floor(Math.random() * 150)
                                );
                                drawStar(ctx, Math.floor(Math.random() * 4) + 2);
                                ctx.restore();
                            }
                        }
                        function drawStar(ctx, r) {
                            ctx.save();
                            ctx.beginPath();
                            ctx.moveTo(r, 0);
                            for (let i = 0; i < 9; i++) {
                                    ctx.rotate(Math.PI / 5);
                                if (i % 2 === 0) {
                                    ctx.lineTo((r / 0.525731) * 0.200811, 0);
                                } else {
                                    ctx.lineTo(r, 0);
                                }
                            }
                            ctx.closePath();
                            ctx.fill();
                            ctx.restore();
                        }
                        
                        makeClip();
                    </script>