preface

Canvas element can be said to be one of the most powerful features in HTML5. Canvas provides a way to draw graphics using JavaScript and HTML < Canvas > elements. It can be used for animation, game graphics, data visualization, image editing, and real-time video processing.

This article is all about 2D direction applications.

CANVAS some lesser-known poses

1. Modify canvas size

By default, the canvas element created by the browser is 300px by 150px. There are two ways to change the canvas size:

  • You can change the size of an element by specifying the width and height attributes.(It is worth noting that while browsers generally allow the use of the PX suffix when specifying width and height attributes, this is technically against the Canvas specification. According to the specification, only non-negative integers can be specified.

  • You can also change the size of the Canvas element using CSS properties.

Changing the size of a Canvas element in this way can have surprising effects. This is because canvas elements actually have two sets of sizes, one is the size of the element itself, and the other is the size of the canvas.

When you set the width and height properties of the canvas, you are actually changing both the element size and the canvas size. Changing the canvas size through CSS will only change the size of the element itself, and will not affect the canvas size.

However, this method will cause the canvas element size to be inconsistent with the canvas size. When the width and height properties do not match the CSS width and height properties, the browser will scale the canvas to fit the element’s size!

2, path

Front knowledge

The basic element of a graph is a path, which is a collection of points of different shapes connected by line segments or curves of different colors and widths. Different from fillRect and strokeRect, other canvas drawing methods are path-based.

When using path drawing, you first define a path and then stroke or fill it. The steps are as follows:

  • The beginPath method is called to start a new path.
  • Then use methods such as rect or ARC to create rectangular or curved paths.
  • Finally, the stroke and fill methods are called to stroke or fill the path you just created.

Path and subpath

At some point, canvas has one and only one path, called the current path. But the current path can have multiple sliver paths, while subpaths are composed of two or more points.

ctx.beginPath();
ctx.rect(1.1.10.10);
ctx.stroke();

ctx.beginPath();
ctx.rect(20.20.10.10)
ctx.stroke();
Copy the code

In the example above, start a new path by calling beginPath, which clears all child paths in the current path. The rect method is then called to add a four-point subpath to the current path. Finally, the stroke method is called to draw the current path.

The second beginPath method clears the previous path to create a new current path.

So the question is, what happens if you get rid of the second beginPath method?

ctx.beginPath();
ctx.rect(1.1.10.10);
ctx.stroke();

ctx.rect(20.20.10.10)
ctx.stroke();
Copy the code

The first step is the same, start a new path by calling beginPath. The rect method is then called to add a four-point subpath to the current path. Finally, the stroke method is called to draw the current path.

Next, the rect method is called again, but since the beginPath method was not called to clear the previous path, the second call adds a subpath to the current path. When the stroke method is finally called again, both stripes of the current path are drawn, so the first rectangle is drawn twice!

Path direction and non-zero wrap rule

If one day, THE UI gives me an abstract design, how should I implement it? For example, how to fill the following diagram with fill.That’s where the non-zero wrap rule comes in.

Let’s start with the easy one. How do I draw a circle? I believe many students want to use the painter algorithm to draw.

context.strokeStyle = 'navy';

context.beginPath();
context.arc(300.170.150.0.Math.PI*2);
context.fillStyle = 'white';
context.fill();
context.stroke();

context.beginPath();
context.arc(300.170.100.0.Math.PI*2);
context.fillStyle = 'navy';
context.fill();
context.stroke();
Copy the code

As shown in the code above

  • First draw the bottom large circle and fill it with navy using fill.
  • Then I drew concentric circles and filled them with the same background color as the canvas.

It does draw the circle, but it’s a little bit harder.

There is an easier and faster way to do this. The Arc method has an optional parameter anticlockwise. If true, draw the arc counterclockwise, otherwise, draw it clockwise.

context.strokeStyle = 'navy';
context.fillStyle = 'navy';
context.beginPath();
context.arc(300.170.150.0.Math.PI*2.false);
context.arc(300.170.100.0.Math.PI*2.true);
context.fill();
context.stroke();
Copy the code

The effect is the same, but the code and performance are much better.

If the current path contains multiple intersecting subpaths, the canvas’s drawing environment variable needs to determine how the current path should be filled when the fill method is called — using the non-zero wrap rule.

  • At any given region in the path, draw a line segment long enough from inside the region so that the end of the line segment falls completely outside the path range.
  • Initialize the counter to 0, and then change the value of the counter every time this line segment intersects the line of the path. Add one if it intersects the clockwise part of the path, subtract one otherwise.
  • If the final calculator value is not zero, the area is in the path, so when you call fill, it is filled. Otherwise, it is not in the current path and will not be populated.

Using the non-zero wrap rule, it is no longer a problem to draw some irregular shapes

Path and Line segment – Draw a 1 pixel line segment

The Canvas drawing environment provides two methods for creating linear paths: moveTo() and lineTo(). Take a look at the following code:

context.lineWidth = 1;
context.beginPath();
context.moveTo(50.10);
context.lineTo(450.10);
context.stroke();

context.beginPath();
context.moveTo(50.5.50.5);
context.lineTo(450.5.50.5);
context.stroke();
Copy the code

The code above draws two line segments, one from (50, 10) to (450, 10), and the other from (50.5, 50.5) to (450.5, 50.5). And set lineWidth to 1, but the result is different.It’s clear that the top line segment is thicker than the bottom line segment. Why is that?

The reason is that when you draw a line segment 1 pixel wide at the boundary of 2 pixels, it actually takes up 2 pixels. Because a Canvas drawing environment object will try to draw half of the pixel to the right of the boundary line and the other half to the left of the boundary line.

However, it is not possible to draw a line segment half a pixel wide over an entire pixel, so the half pixel in the left and right directions is expanded to one pixel.The second line segment is drawn between two pixels so that half of the left and right halves of the center line do not extend. Together, they fill just one pixel wide.

Canvas state saving and restoration

Different from SVG’s drawing system in the reserved mode, Canvas uses the immediate mode to draw graphics, which means that canvas will immediately draw the specified content on the canvas, and will not save the drawn content.

However, Canvas does not completely save “state”. It also has some functions of saving and restoring “state”. For example, the well-known preservation and restoration of attribute state

Save and restore property states

During Canvas development, different drawing property values are often set. In many cases, these properties are only temporarily changed, such as painting the base with a light color, and then painting the base with a dark color. In this case, you need to temporarily modify the fillStyle attribute.

It provides save() and restore() methods to save and restore all properties of the current Canvas drawing environment.

function drawBg (fillStyle) {
    ctx.save();
    ctx.fillStyle = fillStyle
    // draw
    ctx.restore();
}
Copy the code

The save method of the drawing environment pushes the current drawing environment state information to the top of the stack. The corresponding Restore method pops a state message from the top of the stack, restoring the original state.

Therefore, it is a good habit for Canvas development to use Save to push the state information of the drawing environment to the top of the stack before drawing, and use restore to pop up the state information after drawing.

Preservation and restoration of painted surfaces

Unlike save and restore, the next two methods are less well known.

  • getImageData
  • putImageData

These two methods are important functions of the Canvas drawing environment object. They can save and restore the drawing surface itself.

This is useful when you need to dynamically draw or scale some shapes. For example, if you were drawing a square dynamically, what would you do? Maybe the mousemove event cleared the canvas, re-rendered it, and then drew the square. This method does not work well if you have multiple shapes on the canvas.

If you use getImageData and putImageData, it’s pretty simple

// Save the painted surface in the onmouseDown event
canvas.onmousedown = function () {
    currentImageData = ctx.getImageData(0.0, canvas.width, canvas.height); . }// Restore the drawing surface in the onMousemove event, then draw the square
canvas.onmousemove = function () {
    ctx.putImageData(currentImageData, 0.0); // Restore the original surface, delete the drawn square
    createSquare() // Drag the mouse to change the size, dynamically draw the square
}
// Restore the drawing surface in the onmouseup event and draw the final square
canvas.onmouseup = function () {
    ctx.putImageData(currentImageData, 0.0);
    createSquare() // Draw the final size of the square
}
Copy the code

Let’s see what happens

At the end

More canvas content:

  • Canvas animation – a 🐶 running in the grass under the blue sky and white clouds

For more information about 3d orientation webGL, see 🤭 :

  • Webgl column

Creation is not easy, please move your hands to point out a praise.

For more articles, please go to Github, if you like, please click star, which is also a kind of encouragement to the author.

This paper quotes the following information:

  • HTML5 Cavnas Core Technology