Detailed Canvas 01 - Drawing Basic Graphics

Now that we have set up the canvas environment, we can dig into how to draw on the canvas. By the end of this article, you'll have learned how to draw rectangles, triangles, lines, arcs, and curves, becoming familiar with these basic shapes. Before drawing objects to Canvas, you need to master the path. Let's see how to do it.

# grid

Before we start drawing, we need to understand the canvas grid (canvas grid) and coordinate space. The HTML template on the previous page has a canvas element with a width of 150px and a height of 150px. As shown on the right, the canvas element is covered by a grid by default. Generally speaking, one cell in the grid is equivalent to one pixel in the canvas element. The starting point of the grid is the upper left corner (coordinates (0,0)). All elements are positioned relative to the origin. Therefore, the coordinates of the upper left corner of the blue square in the figure are x pixels from the left (X axis) and y pixels from the top (Y axis) (the coordinates are (x, y)). At the end of the lesson we'll translate the origin to different coordinates, rotate the mesh, and scale. Now we still use the original settings.

#draw rectangle

Unlike SVG, canvas only supports two forms of graphics drawing: rectangles and paths (line segments connected by a series of points). All other types of graphics are formed by combining one or more paths. However, we have many methods of path generation that make it possible to draw complex graphics.

First, we go back to drawing the rectangle. Canvas provides three methods to draw rectangles:

fillRect(x, y, width, height)

draw a filled rectangle

strokeRect(x, y, width, height)

draw a rectangle border

clearRect(x, y, width, height)

Clear the specified rectangular area and make the cleared part completely transparent.

Each of the methods provided above contains the same parameters. x and y specify the coordinates of the upper left corner (relative to the origin) of the rectangle drawn on the canvas. width and height set the dimensions of the rectangle.

The draw() function below is obtained from the previous page, now let's use the above three functions.

#Rectangular example

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    ctx.fillRect(25, 25, 100, 100);
    ctx.clearRect(45, 45, 60, 60);
    ctx.strokeRect(50, 50, 50, 50);
  }
}

The fillRect() function draws a black square with a side length of 100px. The clearRect() function erases a 60 x 60px square from the center of the square , and then strokeRect() generates a 50 x 50 square border within the cleared area .

Next we can see the two optional methods of clearRect(), and then we will know how to change the fill color and stroke color of the rendered graphics.

Different from the path function to be introduced in the next section, the above three functions will appear on the canvas immediately after drawing, and take effect immediately.

# rectangle

Three additional methods for drawing rectangles directly on the canvas, as we started with drawing rectangles, also have the rect() method, which adds a rectangle path to the current path.

rect(x, y, width, height)

Draw a rectangle whose upper left corner coordinates are (x, y), width and height are width and height.

When this method is executed, the moveTo() method automatically sets the coordinate parameter (0,0). That is, the current stroke is automatically reset back to the default coordinates.

# draw the path

The basic element of a graph is a path. A path is a collection of points of different shapes connected by line segments or curves of different colors and widths. A path, or even a subpath, is closed. Drawing shapes with paths requires a few extra steps.

  1. First, you need to create the path starting point.
  2. Then you use the drawing command to draw the path.
  3. Afterwards you close the path.
  4. Once the path is generated, you can render the shape by stroking or filling the path area.

Here are the functions to be used:

beginPath()

Create a new path. After it is generated, the graphic drawing command is directed to the path to generate the path.

closePath()

After closing the path, graphics drawing commands are redirected to the context.

stroke()

Draw the outline of the figure through the line.

fill()

Generates a solid shape by filling the content area of ​​the path.

The first step in generating a path is called beginPath(). Essentially, a path is made up of many subpaths, all in a list, and all the subpaths (lines, arcs, etc.) make up the graph. And every time this method is called, the list is cleared and reset, and then we can redraw new graphics.

Note:  The current path is empty, that is, after calling beginPath(), or when the canvas is just created, the first path construction command is usually regarded as moveTo(), no matter what it actually is. For this reason, you almost always specify your starting location after setting the path.

The second step is to call the function to specify the drawing path, which we will see later in this article.

The third is the closed path closePath(), which is not required. This method closes the shape by drawing a line from the current point to the start point. If the graph is already closed, that is, the current point is the starting point, this function does nothing.

Note:  When you call fill() function, all unclosed shapes will be closed automatically, so you don't need to call closePath() function. But it will not be closed automatically when stroke() is called.

# draw a triangle

For example, the code to draw a triangle is as follows:

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.moveTo(75, 50);
    ctx.lineTo(100, 75);
    ctx.lineTo(100, 25);
    ctx.fill();
  }
}

#moving strokes

A very useful function that doesn't actually draw anything and is part of the path list described above is moveTo(). Or you can imagine working on paper, with the tip of a pen or pencil moving from one point to another.

moveTo(x, y)

Move the stroke to the specified coordinates x and y.

After the canvas is initialized or beginPath() is called, you usually set the starting point using the moveTo() function. We can also use moveTo() to draw some discontinuous paths. Take a look at the smiley example below. I marked the place where the moveTo() method (in red line) is used.

You can try it out with the code snippet below. Just copy it to the previous draw() function.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    ctx.beginPath();
    ctx.arc(75, 75, 50, 0, Math.PI * 2, true); // 绘制
    ctx.moveTo(110, 75);
    ctx.arc(75, 75, 35, 0, Math.PI, false);   // 口 (顺时针)
    ctx.moveTo(65, 65);
    ctx.arc(60, 65, 5, 0, Math.PI * 2, true);  // 左眼
    ctx.moveTo(95, 65);
    ctx.arc(90, 65, 5, 0, Math.PI * 2, true);  // 右眼
    ctx.stroke();
  }
}

#line _

To draw a straight line, the method lineTo() needs to be used.

lineTo(x, y)

Draws a line from the current position to the specified x and y position.

This method has two parameters: x and y, representing the point in the coordinate system where the line ends. The start point is relative to the previous drawn path, the end point of the previous path is the next start point, etc. . . The starting point can also be changed by the moveTo() function.

The following example draws two triangles, one filled and the other stroked.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
  var ctx = canvas.getContext('2d');

  // 填充三角形
  ctx.beginPath();
  ctx.moveTo(25, 25);
  ctx.lineTo(105, 25);
  ctx.lineTo(25, 105);
  ctx.fill();

  // 描边三角形
  ctx.beginPath();
  ctx.moveTo(125, 125);
  ctx.lineTo(125, 45);
  ctx.lineTo(45, 125);
  ctx.closePath();
  ctx.stroke();
  }
}

This starts by calling the beginPath() function to prepare to draw a new shape path. Then use the moveTo() function to move to the target position. Then below, the two line segments are drawn to form the two sides of the triangle.

#arc _

To draw an arc or circle, we use the arc() method. Of course you can use arcTo(), but the implementation of this is not so reliable, so we will not introduce it here.

arc(x, y, radius, startAngle, endAngle, anticlockwise)

Draw an arc (circle) with (x, y) as the center and radius as the radius, from startAngle to endAngle, and generate according to the direction given by anticlockwise (the default is clockwise).

arcTo(x1, y1, x2, y2, radius)

Draw an arc according to the given control point and radius, and then connect the two control points with a straight line.

Here is a detailed introduction to the arc method, which has six parameters: x, y are the coordinates of the center of the circle where the arc is drawn. radius is the radius. The startAngle and endAngle parameters define the start and end arcs in radians. These are all referenced to the x-axis. The parameter anticlockwise is a boolean value. When true, it is counterclockwise, otherwise clockwise.

Remarks:  The unit of the angle in the arc() function is radians, not degrees. The js expression of angle and radian:

Radians = (Math.PI/180)*angles.

The following example is a bit more complicated than the above, and 12 different angles and filled arcs are drawn below.

The following two for loops generate the row and column (x, y) coordinates of the arc. BeginPath() is called at the beginning of each arc. In the code, the parameters of each arc are variable, but in actual programming, we don't need to do this.

The x,y coordinates are variable. Both the radius (radius) and the start angle (startAngle) are fixed. The end angle (endAngle) starts at 180 degrees in the first column (half circle) and increases by 90 degrees with each column. The last column forms a complete circle.

The clockwise statement acts on the first and third lines as a clockwise arc, and anticlockwise acts on the second and fourth lines as a counterclockwise arc. The if statement makes the first and second lines stroke the arc, and the next two lines fill the path.

Note:  The required canvas size for this example is slightly larger than the other examples on this page: 150 x 200 pixels.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    for(var i = 0; i < 4; i++){
      for(var j = 0; j < 3; j++){
        ctx.beginPath();
        var x = 25 + j * 50; // x 坐标值
        var y = 25 + i * 50; // y 坐标值
        var radius = 20; // 圆弧半径
        var startAngle = 0; // 开始点
        var endAngle = Math.PI + (Math.PI * j) / 2; // 结束点
        var anticlockwise = i % 2 == 0 ? false : true; // 顺时针或逆时针

        ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

        if (i>1){
          ctx.fill();
        } else {
          ctx.stroke();
        }
      }
    }
  }
}

#Use the arcTo method

Here is a simple code snippet to draw an arc. The base point is blue and the two control points are red.

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

ctx.setLineDash([])
ctx.beginPath();
ctx.moveTo(150, 20);
ctx.arcTo(150,100,50,20,30);
ctx.stroke();

ctx.fillStyle = 'blue';
// base point
ctx.fillRect(150, 20, 10, 10);

ctx.fillStyle = 'red';
// control point one
ctx.fillRect(150, 100, 10, 10);
// control point two
ctx.fillRect(50, 20, 10, 10);
//
ctx.setLineDash([5,5])
ctx.moveTo(150, 20);
ctx.lineTo(150,100);
ctx.lineTo(50, 20);
ctx.stroke();
ctx.beginPath();
ctx.arc(120,38,30,0,2*Math.PI);
ctx.stroke();

#Quadratic Bezier curve and cubic Bezier curve

The next very useful type of path is the Bézier curve. Both quadratic and cubic Bezier curves are very useful and are generally used to draw complex and regular graphics.

quadraticCurveTo(cp1x, cp1y, x, y)

Draw a quadratic Bezier curve, cp1x,cp1y is a control point, x,y is the end point.

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

Draw a cubic Bezier curve, cp1x, cp1y is control point 1, cp2x, cp2y is control point 2, x, y is the end point.

The figure on the right can well describe the relationship between the two. The quadratic Bezier curve has a start point (blue), an end point (blue) and a control point (red), while the cubic Bezier curve has Two control points.

The parameters x, y are the end point coordinates in both methods. cp1x, cp1y is the first control point in coordinates, cp2x, cp2y is the second control point in coordinates.

Working with quadratic and cubic Bézier curves is somewhat difficult because unlike vector software like Adobe Illustrators, the curves we draw don't give us direct visual feedback. This makes it very difficult to draw complex graphics. In the following examples, we will draw some simple and regular graphics. If you have time and more patience, you can also draw many complex graphics.

The following examples do not pose much difficulty. In these two examples, we will draw Bezier curves continuously, and finally form complex graphics.

#Quadratic Bezier curve

This example uses multiple Bezier curves to render a speech bubble.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    // 二次贝塞尔曲线
    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();
   }
}

# Cubic Bezier curve

This example draws a heart shape using Bézier curves.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

     //三次贝塞尔曲线
    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();
  }
}

#

#Combined use

Each shape in each of the examples so far has used only one type of path. However, drawing a graph does not limit the number and type of usage. So in one final example, let's combine all the path functions to reproduce a famous game.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var 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(var i = 0; i < 8; i++){
      ctx.fillRect(51 + i * 16, 35, 4, 4);
    }

    for(i = 0; i < 6; i++){
      ctx.fillRect(115, 51 + i * 16, 4, 4);
    }

    for(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();
  }
}

// 封装的一个用于绘制圆角矩形的函数。

function roundedRect(ctx, x, y, width, height, radius){
  ctx.beginPath();
  ctx.moveTo(x, y + radius);
  ctx.lineTo(x, y + height - radius);
  ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
  ctx.lineTo(x + width - radius, y + height);
  ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
  ctx.lineTo(x + width, y + radius);
  ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
  ctx.lineTo(x + radius, y);
  ctx.quadraticCurveTo(x, y, x, y + radius);
  ctx.stroke();
}

We won't explain the code above in great detail, because it's actually quite easy to understand. The key point is that the fillStyle property and the encapsulation function (roundedRect() in the example) are used in the drawing context. Using wrapper functions is very useful for reducing code size and complexity.

We'll discuss fillStyle in more detail later in the lesson. In this chapter, all we did with the fillStyle style was change the fill color from the default black to white and then black again.

# Path2D object

As we saw in the previous example, you can use a series of path and drawing commands to "paint" objects on the canvas. To simplify code and improve performance, Path2D objects are available in newer browsers to cache or record drawing commands so you can quickly review paths.

How to generate a Path2D object?

Path2D()

Path2D() returns a newly initialized Path2D object (possibly with a path as a variable - creating a copy of it, or with a string containing the SVG path data as a variable).

All path methods such as moveTo, rect, arc or quadraticCurveTo etc., as we have seen before, can be used in Path2D.

The Path2D API adds addPath as a method to combine paths. This is useful when you want to create objects from several elements. for example:

Path2D.addPath(path [, transform])

Adds a path to the current path (possibly adding a transformation matrix).

# Path2D example

In this example, we created a rectangle and a circle. They are all stored as Path2D objects to be used later. With the new Path2D API, several methods were updated to use the Path2D object instead of the current path. Here, stroke and fill with path parameters can draw objects on the canvas.

function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext){
    var ctx = canvas.getContext('2d');

    var rectangle = new Path2D();
    rectangle.rect(10, 10, 50, 50);

    var circle = new Path2D();
    circle.moveTo(125, 35);
    circle.arc(100, 35, 25, 0, 2 * Math.PI);

    ctx.stroke(rectangle);
    ctx.fill(circle);
  }
}

#Use SVG paths

Another powerful feature of the new Path2D API is the use of SVG path data to initialize paths on the canvas. This will allow you to retrieve paths and reuse them as SVG or canvas.

This path will first move to the point (M10 10) then 80 units horizontally (h 80), then 80 units down (v 80), then 80 units left (h -80), and back to at the starting point (z).

var p = new Path2D("M10 10 h 80 v 80 h -80 Z");

Guess you like

Origin blog.csdn.net/qq_59747594/article/details/131350748