Canvas

Canvas

Overview

The <canvas> element is used to generate images. It is like a canvas itself, and JavaScript generates images by operating its API. Its underlying is a collection of pixels, basically <canvas> is a bitmap that can be operated by JavaScript.
Before using the Canvas API, you need to create a <canvas> element in the web page.
HTML
<canvas id="myCanvas" width="400" height="250">
  Your browser does not support Canvas
</canvas>
If the browser does not support this API, it will display the text in the middle of the <canvas> tag: "Your browser does not support Canvas".
Each <canvas> element has a corresponding CanvasRenderingContext2D object (context object). The Canvas API is defined on this object.
JavaScript
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
In the above code, the getContext() method of the <canvas> element node returns the CanvasRenderingContext2D object.
Note that the Canvas API requires the parameter 2d in getContext to specify that this <canvas> node generates 2D plane images. If the parameter is webgl, it indicates generating 3D stereoscopic patterns, which is part of the WebGL API.
According to usage, the Canvas API is divided into two major parts: drawing graphics and image processing.
 
SVG vs Canvas
It differs from SVG images in that <canvas> generates images by calling various methods with JavaScript, while SVG is an XML file that generates images through various child elements.
 
DOM vs Canvas
The primary difference between DOM (Document Object Model) and Canvas is the way in which they are rendered. DOM is a retained mode, meaning that it holds elements and renders them according to the instructions given. In contrast, Canvas is an immediate mode, meaning that it renders elements based on the instructions given at the time of drawing.
When it comes to drawing and rendering, DOM is typically slower than Canvas. This is because DOM needs to be re-rendered every time something is changed, whereas Canvas only needs to be rendered once and the changes are applied immediately.
Another key difference is that DOM is a hierarchical structure of elements, whereas Canvas is a flat structure of pixels. This means that DOM elements can be manipulated and styled easily, while Canvas elements require more complex manipulation and styling.
Retained Mode (DOM)
notion image
 
 
Immediate Mode (Canvas)
notion image
 

Drawing properties

Java
// Generic
ctx.globalAlpha = 0.2;
// clear a Arc area
// clearRect(x, y, width, height)
ctx.clearRect(0,0,canvas.width,canvas.height);

// stroke
ctx.lineWidth = 5 ;
ctx.strokeStyle = 'green';
ctx.linecap = 'butt'|'round'|'square';
ctx.lineJoin = 'miter'|'bevel'|'round';
ctx.setLineDash([5, 15]);

// fill
ctx.fillStyle = 'red';

// text
ctx.font = '30px Areial';

// Path
ctx.beginPath();
ctx.moveTo(50,50);
ctx.lineTo(150,50);
ctx.lineTo(100,200);
// draw path
ctx.stroke();

//fill path
ctx.fill();

ctx.closePath();

// Draw circle
// need to call beginPath if you don't want previous path is connected
// to the circle
// arc(x, y, radius, startAngle, endAngle, anticlockwise=false)
ctx.beginPath();
ctx.arc(100,100,40,0,Math.PI *2);
ctx.arc(x,y,radius,startAngle,endAngle,antclockwise);
ctx.stroke();
ctx.closePath();

Generic

Lines

  • CanvasRenderingContext2D.lineWidth: Specifies the width of the line, default is 1.0.
  • CanvasRenderingContext2D.lineCap: Specifies the style of the line end, with three possible values: butt (default, end is rectangular), round (end is rounded), square (end is protruding rectangle, rectangle width unchanged, height is half the line width).
  • CanvasRenderingContext2D.lineJoin: Specifies the style of the line segment intersection, with three possible values: round (intersection is rounded), bevel (intersection is triangular bottom edge), miter (default, intersection is diamond).
  • CanvasRenderingContext2D.miterLimit specifies the maximum length of the miter join, which is the diagonal join created when two line segments meet at an acute angle. A miter join longer than this limit will be converted to a bevel join.
  • CanvasRenderingContext2D.getLineDash() returns an array showing the lengths of dashes and spaces in a dashed line.
  • CanvasRenderingContext2D.setLineDash() sets the dash pattern for dashed lines. An array is passed indicating the lengths of dashes and spaces.
  • ctx.arcTo() creates an arc/curve (part of a circle) between two tangents.
  • ctx.bezierCurveTo() creates a cubic Bézier curve. Four points are passed: start control point x, start control point y, end control point x, end control point y.
  • ctx.quadraticCurveTo() creates a quadratic Bézier curve. Three points are passed: control point x, control point y, end point x, end point y.
The example shows using ctx.bezierCurveTo() to draw a curved line.
JavaScript
ctx.setLineDash([]);
ctx.setLineDash([1, 1]);
ctx.setLineDash([10, 10]);
ctx.setLineDash([20, 5]);
ctx.setLineDash([15, 3, 3, 3]);
ctx.setLineDash([20, 3, 3, 3, 3, 3, 3, 3]);
ctx.setLineDash([12, 3, 3]); // Equals [12, 3, 3, 12, 3, 3]
notion image

Gradient color

The following methods are used to set gradient effects and image filling effects.
  • CanvasRenderingContext2D.createLinearGradient(): Defines a linear gradient style.
  • CanvasRenderingContext2D.createRadialGradient(): Defines a radial gradient style.
  • CanvasRenderingContext2D.createPattern(): Defines an image filling style.
JavaScript
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');

var gradient = ctx.createLinearGradient(0, 0, 600, 0)
gradient.addColorStop(0, 'green')
gradient.addColorStop(1, 'red')
ctx.fillStyle = gradient
ctx.fillRect(100, 100, 100, 100);
notion image
The code above defines a gradient style gradient and then specifies this style for the fillStyle property. Then fillRect() will generate a filled rectangle area with this style.
The createRadialGradient() method defines a radial gradient and requires specifying two circles.
ctx.createRadialGradient(x0, y0, r0, x1, y1, r1)
The createRadialGradient() method accepts six parameters, x0 and y0 are the center coordinates of the starting circle of the gradient, r0 is the radius of the starting circle, x1 and y1 are the center coordinates of the ending circle of the gradient, r1 is the radius of the ending circle.
The return value of this method is also a CanvasGradient object
JavaScript
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

const gradient = ctx.createRadialGradient(100, 100, 50, 100, 100, 100);
gradient.addColorStop(0, 'white');
gradient.addColorStop(1, 'green');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 200, 200);
The code above generates a radial style and then fills a rectangle with this style.

Image Filling

The createPattern() method defines an image fill style that repeats the image continuously in a specified direction to fill a specified area.
ctx.createPattern(image, repetition)
This method accepts two parameters:
  1. The image data, which can be an <img> element, another <canvas> element, or a Blob object representing the image.
  1. A string with four possible values:
      • repeat (repeat in both directions)
      • repeat-x (repeat horizontally)
      • repeat-y (repeat vertically)
      • no-repeat (do not repeat).
 
In the above code, after the image is loaded, the createPattern() method is used to generate an image style, and then this style is used to fill a specified area.

Shadows

The following properties are used to set shadows.
  • CanvasRenderingContext2D.shadowBlur: The blur level of the shadow, default is 0.
  • CanvasRenderingContext2D.shadowColor: The color of the shadow, default is black.
  • CanvasRenderingContext2D.shadowOffsetX: The horizontal offset of the shadow, default is 0.
  • CanvasRenderingContext2D.shadowOffsetY: The vertical offset of the shadow, default is 0.
notion image
 

Draw text

JavaScript
context.fillStyle = "gray";
const fontSize = 20;
context.font = `bold ${fontSize}px Arial`;
context.textBaseline = "top";

// According to the previous setting, measure the text width
context.measureText("Hello World");

context.textAlign = "left"|"right"|"center";

// Draw text at position, when textBaseline is top, it is the top left
// Default is left bottom
context.fillText("Hello World", 0, 0);
Methods:
  • CanvasRenderingContext2D.fillText():Draw solid characters at a specified position.
  • CanvasRenderingContext2D.strokeText():Draw hollow characters at a specified position.
  • CanvasRenderingContext2D.measureText():Returns a TextMetrics object with measurements of the text.
  • CanvasRenderingContext2D.font:Specify font size and font, default is10px sans-serif.
  • CanvasRenderingContext2D.textAlign:Text alignment, default isstart.
  • CanvasRenderingContext2D.direction:Text direction, default isinherit.
  • CanvasRenderingContext2D.textBaseline:Vertical position of text, default isalphabetic.
 
CanvasRenderingContext2D.fillText(text, x, y [, maxWidth])
This method accepts three parameters:
  • text:The string to be filled.
  • x:The horizontal coordinate of the text start point, in pixels.
  • y:The vertical coordinate of the text start point, in pixels.
  • maxWidth (optional):The maximum pixel width of the text. If omitted, the width is unlimited. If the actual text length exceeds this value, the browser will try to fill it with a smaller font.
💡
The fillText() method does not support text line breaks, all text must appear on one line. If you want to generate multiline text, you only need to call fillText() multiple times.
💡
The text drawn by the above two methods is default 10px size, sans-serif font, and the font attribute can change the font settings. The value of this attribute is a string, which can be used with the CSS font attribute.
The textAlign property specifies how text is aligned. It has the following values:
  • left: Left alignment
  • right: Right alignment
  • center: Centered
  • start: Default, starts alignment (text from left to right is left aligned, text from right to left is right aligned)
  • end: Ends alignment (text from left to right is right aligned, text from right to left is left aligned)
 
The direction attribute specifies the text's direction, defaulting to inherit from the <canvas> or document. Other values include ltr (left to right) and rtl (right to left).
 
The textBaseline attribute specifies the text's vertical position. It has these values:
  • top: Top alignment (the baseline of letters shifts up as a whole).
  • hanging: Hanging alignment (the upper edge of letters lies on a straight line), suitable for Indian and Tibetan scripts.
  • middle: Middle alignment (the middle line of letters lies on a straight line).
  • alphabetic: Default, indicating letters occupy the normal alphabet table position (on the third line of a four-line grid).
  • ideographic: Bottom edge alignment (the bottom edge of letters lies on a straight line), used for East Asian scripts.
  • bottom: Bottom alignment (the baseline of letters shifts down). For English letters, this differs from ideographic only.
 
The measureText() method accepts a string parameter and returns a TextMetrics object, providing information about the string's parameters after rendering, mainly its width.
JavaScript
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var text1 = ctx.measureText('Hello world');
text1.width // 55.14

ctx.font = 'Bold 20px Arial';
var text2 = ctx.measureText('Hello world');
text2.width // 107.78

Useful packages:

Computing accurate font metrics such as x-height, cap height, ascent, descent and tittle for any loaded web font.
FontMetrics
soulwireUpdated Dec 25, 2023

Image transformation

The following methods are used for image transformation.
  • CanvasRenderingContext2D.rotate(): Image rotation
  • CanvasRenderingContext2D.scale(): Image scaling
  • CanvasRenderingContext2D.translate(): Image translation
  • CanvasRenderingContext2D.transform(): Complete image transformation through a transformation matrix
  • CanvasRenderingContext2D.setTransform(): Cancel the previous image transformation
 

Rotate

The CanvasRenderingContext2D.rotate() method is used for image rotation. It accepts a radian value as a parameter, indicating the number of degrees rotated clockwise.
JavaScript
const ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.rotate(45 * Math.PI / 180);
ctx.fillRect(70, 0, 100, 30);
notion image
💡
The above code will display a 45 degree clockwise tilted rectangle. Note that the rotate() method must be called before the fillRect() method, otherwise it will not work.
💡
The rotation center point is always the origin of the upper left corner of the canvas. If you want to change the center point, you need to use the translate() method to move the canvas.

Scale

CanvasRenderingContext2D.scale() method is used to scale images. It accepts two parameters, namely the scaling factor in the x-axis direction and the scaling factor in the y-axis direction. By default, one unit is one pixel, and the scaling factor can scale units, such as a scaling factor of 0.5 means reducing the size to 50% of the original, and a scaling factor of 10 means magnifying ten times.
JavaScript
const ctx = canvas.getContext("2d");
ctx.fillStyle = "blue";
ctx.fillRect(10, 10, 20, 20);
ctx.fillStyle = "red";
ctx.scale(5, 2);
ctx.fillRect(10, 10, 20, 20);
notion image
The original rectangle in the above code is 20 x 20, and after scaling it is displayed as 100 x 40.
💡
If the scaling factor is 1, it means the image has not been scaled at all. If it is -1, it means the direction has been reversed. ctx.scale(-1, 1) means horizontal flipping, and ctx.scale(1, -1) means vertical flipping.

translate

The CanvasRenderingContext2D.translate() method is used to translate an image. It accepts two parameters, the distances to move along the x-axis and y-axis respectively (in pixels).
JavaScript
const ctx = canvas.getContext("2d");
ctx.fillStyle = "blue";
ctx.fillRect(10, 10, 20, 20);
ctx.fillStyle = "red";
ctx.translate(50, 50);
ctx.fillRect(10, 10, 20, 20);
notion image
 

Transform

CanvasRenderingContext2D.transform() method accepts six elements of a transformation matrix as parameters to complete scaling, rotation, movement and skewing.
Its usage format is as follows.
ctx.transform(a, b, c, d, e, f);
  • a: Horizontal scaling (default value 1, unit multiple)
  • b: Horizontal skewing (default value 0, unit radians)
  • c: Vertical skewing (default value 0, unit radians)
  • d: Vertical scaling (default value 1, unit multiple)
  • e: Horizontal offset (default value 0, unit pixels)
  • f: Vertical offset (default value 0, unit pixels)
💡
Note that multiple transform() methods have cumulative effects.

setTransform

CanvasRenderingContext2D.setTransform() method cancels the previous graphics transformation and restores the canvas to the state specified by the method. The parameters of this method are completely consistent with the transform() method.
JavaScript
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');

ctx.translate(50, 50);
ctx.fillRect(0, 0, 100, 100);

ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillRect(0, 0, 100, 100);
In the above code, the first fillRect() method draws a rectangle with its top left corner translated from (0, 0) to (50, 50). The setTransform() method cancels this transformation (the drawn graphics are not affected) and restores the canvas to the default state (the transformation matrix 1, 0, 0, 1, 0, 0), so the top left corner of the second rectangle returns to (0, 0).

Image composite

Image

Canvas API allows you to write image files to the canvas by reading the image first and then using the drawImage() method to put the image on the canvas.
CanvasRenderingContext2D.drawImage() has three usage formats.
ctx.drawImage(image, dx, dy);
ctx.drawImage(image, dx, dy, dWidth, dHeight);
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
The meanings of the various parameters are as follows.
  • image: image element
  • sx: The horizontal coordinate of the image internally used to map to the placement point on the canvas.
  • sy: The vertical coordinate of the image internally used to map to the placement point on the canvas.
  • sWidth: The width of the image on the canvas, resulting in a scaling effect. If not specified, the image will not be scaled and will occupy the width of the canvas in its actual size.
  • sHeight: The height of the image on the canvas, resulting in a scaling effect. If not specified, the image will not be scaled and will occupy the height of the canvas in its actual size.
  • dx: The horizontal coordinate on the canvas used to place the left top corner of the image
  • dy: The vertical coordinate on the canvas used to place the right top corner
  • dWidth: The width of the image within the canvas, resulting in a scaling effect.
  • dHeight: The height of the image within the canvas, resulting in a scaling effect.
Here is the simplest usage scenario, placing an image on the canvas with the left top corners aligned.
JavaScript
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');

var img = new Image();
img.src = 'image.png';
img.onload = function () {
  ctx.drawImage(img, 0, 0);
};

Save to Image

JavaScript
// quality from 1/0
canvas.toDataURL('image/jpeg', quality)

ctx.getImageData(0, 0, canvas.width, canvas.height);
ctx.putImageData(imageData, 0, 0);

Advanced Topic

Canvas Pixel Manipulations API

JavaScript
const imagedata = ctx.createImageData(200, 200);
ctx.putImageData(imagedata,dx,dy)

Player

JavaScript
const image = document.getElementByID('source');
const player = {
	w:50,
	h:70,
	x:20,
	y:200,
	speed:5,
	dx:0,
	dy:0,
}

function drawPlayer(){
	ctx.drawImage(image,player.x,player.y); 
}
function clear(){
	ctx.clearRect(0,0,canvas.width,canvas.height);
}

function newPos(){
	player.x += player.dx;
	player.y += player.dy;
}

function update(){
	clear();
  newPos();
	drawPlayer();
}

update();

function keyDown(e){
	if(e.key === 'ArrowRight' || e.key === 'Right'){
		moveRight();
   }else 
     ....
}


document.addEventListener('keydown',keyDown):
document.addEventListener('keyup',keyUp);
 
globalAlpha (0 to 1)
0 transparent

Methods to clear canvas:

  • Simple fill a new background colour
  • Resetting the Canvas Width and Height
  • Resetting the Canvas clearRect Functioncontext.clearRect(0,0,w,h);

Pitfall

When drawing lines, the right line is unclear, since it crosses two pixel.
If you need to set the size of a canvas using CSS, while ensuring that the drawn shapes do not become distorted, you can set the width and height of the canvas to the corresponding CSS width and height. This way, the drawn shapes will not become distorted.
HTML
<style>
	canvas {
    width: 600px;
    height: 600px;
  }
</style>
<body>
  <div id="root">
    <canvas id="canvas" class="canvas"></canvas>
  </div>
  <script>
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    canvas.width = "600px";
    canvas.height = "600px";
  </script>
</body>
When using canvas to draw graphics, it is necessary to use window.devicePixelRatio to correct the canvas resolution. This ensures that the size of the drawn graphics is correct, clear, and not distorted. When using canvas, it is essential to correct the resolution.
HTML
<style>
  canvas {
    width: 600px;
    height: 600px;
  }
</style>
<body>
  <div id="root">
    <canvas id="canvas"> </canvas>
  </div>
  <script>
    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    const { offsetWidth, offsetHeight } = canvas;

    canvas.width = offsetWidth * window.devicePixelRatio;
    canvas.height = offsetHeight * window.devicePixelRatio;
    ctx.scale(window.devicePixelRatio, window.devicePixelRatio);

    ctx.strokeStyle = "red";
    ctx.lineWidth = 1;
    ctx.strokeRect(0, 0, 100, 100);
  </script>
</body>

Reference

Tutorial: