Canvas API

An overview of the

The Canvas API is used to generate images on web pages in real time and manipulate the content of the images. It is basically a bitmap that can be manipulated using JavaScript.


Begin to use

  • First, create a new one<canvas>Page elements.
<canvas id="myCanvas" width="400" height="200">Your browser does not support Canvas!</canvas>
Copy the code

In the above code, if the browser does not support this API, the text in the middle of the < Canvas > TAB will be displayed — “Your browser does not support Canvas!” .

  • Each Canvas node has a corresponding context object (context object). The Canvas API is defined on this context object, so you need to get this object by using the getContext method.
var canvas = document.getElementById('myCanvas');

if (canvas.getContext) {
  var ctx = canvas.getContext('2d');
}
Copy the code

In the above code, the getContext method specifies the parameter 2D, indicating that the Canvas node is used to generate a 2D (i.e. flat) pattern. If the parameter is WebGL, it means used to generate 3D images (that is, stereoscopic patterns), and this part is actually called the WebGL API alone.


The drawing method

Canvas the canvas provides a flat space for drawing. Each point in the space has its own coordinate. X is the horizontal coordinate and y is the vertical coordinate. The origin (0, 0) is in the upper left corner of the image. The forward direction of the X-axis means that the origin is to the right, and the forward direction of the Y-axis means that the origin is downwards

(1) Draw path

The beginPath method means to start drawing the path, the moveTo(x, y) method sets the starting point of the line segment, the lineTo(x, y) method sets the end point of the line segment, and the stroke method is used to color transparent line segments.

ctx.beginPath(); // Start path drawing
ctx.moveTo(20.20); // set the starting point of the path to (20,20)
ctx.lineTo(200.20); // draw a line to (200,20)
ctx.lineWidth = 1.0; // Set the line width
ctx.strokeStyle = '#CC0000'; // Set the color of the line
ctx.stroke(); // Color the line so that the entire line becomes visible
Copy the code

The moveto and lineto methods can be used multiple times. Finally, you can use the closePath method to automatically draw a line from the current point to the starting point to form a closed graph, instead of using the lineto method once.

(2) Draw a rectangle

The fillRect(x, y, width, height) method is used to draw a rectangle. Its four arguments are the x and y coordinates of the vertex in the upper left corner of the rectangle, and the width and height of the rectangle. The fillStyle property is used to set the fill color of the rectangle.

ctx.fillStyle = 'yellow';
ctx.fillRect(50.50.200.100); 
Copy the code

The strokeRect method is similar to fillRect in drawing a hollow rectangle.

ctx.strokeRect(10.10.200.100); 
Copy the code

The clearRect method clears the contents of a rectangular area.

ctx.clearRect(100.50.50.50); 
Copy the code

(3) Draw text

FillText (string, x, y) is used to draw text. Its three arguments are the text content and the starting x and y coordinates. Before use, use font to set the font, size, and style (write similar to the CSS font property). Similarly, the strokeText method is used to add hollow words.

// Set the font
ctx.font = "Bold 20px Arial"; 
// Set the alignment
ctx.textAlign = "left";
// Set the fill color
ctx.fillStyle = "# 008600"; 
// Set the font content, and the position on the canvas
ctx.fillText("Hello!".10.50); 
// Draw hollow words
ctx.strokeText("Hello!".10.100); 
Copy the code

The fillText method does not support text line breaking, that is, all text appears on one line. So, if you want to generate multiple lines of text, you have to call the fillText method multiple times.

(4) Draw the circle and fan

The ARC method is used to draw the sector

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

The x and Y parameters of the ARC method are the center coordinates, radius is the radius, startAngle and endAngle are the start and end angles of the fan (in radians), and anticlockwise indicates whether the graph should be drawn counterclockwise (true) or clockwise (false).

Here’s how to draw a solid circle.

ctx.beginPath(); 
ctx.arc(60.60.50.0.Math.PI*2.true); 
ctx.fillStyle = "# 000000"; 
ctx.fill();
Copy the code

Example of drawing a hollow circle.

ctx.beginPath(); 
ctx.arc(60.60.50.0.Math.PI*2.true); 
ctx.lineWidth = 1.0; 
ctx.strokeStyle = "# 000"; 
ctx.stroke();
Copy the code

(5) Set the gradient

The createLinearGradient method is used to set the gradient.

var myGradient = ctx.createLinearGradient(0.0.0.160); 

myGradient.addColorStop(0."#BABABA"); 

myGradient.addColorStop(1."# 636363");
Copy the code

The parameters of the createLinearGradient method are (x1, y1, x2, y2), where x1 and y1 are the starting coordinates and x2 and y2 are the ending coordinates. With different coordinate values, you can generate gradients from top to bottom, left to right, and so on.

The use method is as follows:

ctx.fillStyle = myGradient;
ctx.fillRect(10.10.200.100);
Copy the code

(6) Set the shadow

A series of shadow-related methods that you can use to set shadows.

ctx.shadowOffsetX = 10; // Set the horizontal displacement
ctx.shadowOffsetY = 10; // Set the vertical displacement
ctx.shadowBlur = 5; // Set the blur
ctx.shadowColor = "Rgba (0,0,0,0.5)"; // Set the shadow color

ctx.fillStyle = "#CC0000"; 
ctx.fillRect(10.10.200.100);
Copy the code

Image processing method

DrawImage method

The Canvas API allows you to insert an image file into the Canvas by reading the image and redrawing it inside the Canvas using the drawImage method.

var img = new Image();
img.src = 'image.png';
ctx.drawImage(img, 0.0); // Set the corresponding image object and its position on the canvas
Copy the code

The above code loads a PNG image onto the canvas. The drawImage() method takes three arguments. The first argument is the DOM element of the image file (that is, the node). The second and third arguments are the coordinates of the upper-left corner of the image on the canvas.

Since the image takes time to load, the drawImage method can only be called after the image is fully loaded, so the above code needs to be rewritten.

var image = new Image();

image.onload = function() {
  var canvas = document.createElement('canvas');
  canvas.width = image.width;
  canvas.height = image.height;
  canvas.getContext('2d').drawImage(image, 0.0);
  // Insert bottom of page
  document.body.appendChild(image);
  return canvas;
}

image.src = 'image.png';
Copy the code

GetImageData method, putImageData method

The getImageData method can be used to read the content of the Canvas and return an object containing information about each pixel.

var imageData = context.getImageData(0.0, canvas.width, canvas.height);
Copy the code

The imageData object has a data property whose value is a one-dimensional array. The values of the array, in turn, are the red, green, blue, and alpha channel values for each pixel, so the length of the array is equal to the pixel width of the image x the pixel height of the image x 4, and each value ranges from 0 to 255. This array is not only readable, but also writable, so by manipulating the values of this array, you can manipulate the image. After you modify the array, use the putImageData method to redraw the contents of the array onto the Canvas.

context.putImageData(imageData, 0.0);
Copy the code

ToDataURL method

After the image data is modified, the toDataURL method can be used to convert the Canvas data back into the general image file form.

function convertCanvasToImage(canvas) {
  var image = new Image();
  image.src = canvas.toDataURL('image/png');
  return image;
}
Copy the code

The above code converts the Canvas data to a PNG data URI.

Save method, restore method

The save method is used to save the context, and the restore method is used to restore to the last saved context.

ctx.save();

ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 5;
ctx.shadowColor = 'rgba (0,0,0,0.5)';

ctx.fillStyle = '#CC0000';
ctx.fillRect(10.10.150.100);

ctx.restore();

ctx.fillStyle = '# 000000';
ctx.fillRect(180.10.150.100);
Copy the code

The above code saves the current setting with the save method, and then draws a shaded rectangle. Next, using the restore method, you restore the Settings before saving and draw a rectangle without shadows.


animation

With JavaScript, you can easily animate the canvas element.

var posX = 20,
    posY = 100;

setInterval(function() {
	context.fillStyle = "black";
    context.fillRect(0.0,canvas.width, canvas.height);

	posX += 1;
	posY += 0.25;

	context.beginPath();
	context.fillStyle = "white";

	context.arc(posX, posY, 10.0.Math.PI*2.true); 
	context.closePath();
	context.fill();
}, 30);
Copy the code

The above code creates the effect of a small dot moving down and to the right every 30 milliseconds. At the beginning of the setInterval function, the reason why the canvas is re-rendered with a black background is to erase the dots from the previous step.

By setting the coordinates of the center of the circle, various motion paths can be generated.

First up and then down.

var vx = 10,
    vy = - 10,
    gravity = 1;

setInterval(function() {
    posX += vx;
    posY += vy;
    vy += gravity;
	// ...
});
Copy the code

In the above code, the x-coordinate keeps increasing, indicating that it keeps moving to the right. The y coordinate gets smaller, and then it gets bigger and bigger under gravity, so it goes up and then it goes down.

The ball bounced and went to rest.

var vx = 10,
    vy = - 10,
    gravity = 1;

setInterval(function() {
    posX += vx;
    posY += vy;

	if (posY > canvas.height * 0.75) {
          vy *= 0.6;
          vx *= 0.75;
          posY = canvas.height * 0.75;
    }
	
    vy += gravity;
	// ...
});
Copy the code

The above code says that once the ball’s y-coordinate is at the bottom 75% of the screen, it moves 75% as fast as it did in the X-axis and 40% as fast as it bounced in the Y-axis.


Pixel processing

Through the getImageData method and putImageData method, each pixel can be processed to manipulate the image content.

Assuming that filter is a function that processes pixels, the entire process of Canvas processing can be represented by the following code.

if (canvas.width > 0 && canvas.height > 0) {

	var imageData = context.getImageData(0.0, canvas.width, canvas.height);

    filter(imageData);

    context.putImageData(imageData, 0.0);

}
Copy the code

Here are some common ways to do this.

Gray scale effect

A grayscale is the arithmetic mean of the red, green, and blue pixel values, which actually turns the image into black and white. Suppose d[I] is the red value of one pixel in the pixel array, then D [I +1] is the green value, D [I +2] is the blue value, and D [I +3] is the alpha channel value. The grayscale algorithm is to add the values of red, green, and blue, divide by three, and write the result back into the array.

grayscale = function (pixels) {

	var d = pixels.data;

    for (var i = 0; i < d.length; i += 4) {
      var r = d[i];
      var g = d[i + 1];
      var b = d[i + 2];
      d[i] = d[i + 1] = d[i + 2] = (r+g+b)/3;
    }

    return pixels;

};
Copy the code

Effect of restoring ancient ways

Sepia is a weighted average of the three pixels, red, green and blue, to give the image an antique effect.

sepia = function (pixels) {

    var d = pixels.data;

    for (var i = 0; i < d.length; i += 4) {
      var r = d[i];
      var g = d[i + 1];
      var b = d[i + 2];
      d[i]     = (r * 0.393)+(g * 0.769)+(b * 0.189); // red
      d[i + 1] = (r * 0.349)+(g * 0.686)+(b * 0.168); // green
      d[i + 2] = (r * 0.272)+(g * 0.534)+(b * 0.131); // blue
    }

    return pixels;

};
Copy the code

Red mask effect

The red mask means to give the image a reddish effect. The algorithm is to set the red channel to the average of the three values of red, green and blue, and set the green channel and blue channel to 0.

red = function (pixels) {
	
    var d = pixels.data;

    for (var i = 0; i < d.length; i += 4) {
      var r = d[i];
      var g = d[i + 1];
      var b = d[i + 2];
      d[i] = (r+g+b)/3;        // The red channel is averaged
      d[i + 1] = d[i + 2] = 0; // Set both green and blue channels to 0
    }

    return pixels;

};
Copy the code

Effect of brightness

A brightness effect, “brightness,” means to make an image brighter or darker. The algorithm adds a positive or negative value to the red channel, the green channel, and the blue channel.

brightness = function (pixels, delta) {

    var d = pixels.data;

    for (var i = 0; i < d.length; i += 4) {
          d[i] += delta;     // red
          d[i + 1] += delta; // green
          d[i + 2] += delta; // blue   
    }

	return pixels;

};
Copy the code

Reversal effect

Invert, “invert,” means that the image has an inverted color effect. The algorithm takes the opposite value (255- original value) for the red, green and blue channels.

invert = function (pixels) {

	var d = pixels.data;

	for (var i = 0; i < d.length; i += 4) {
		d[i] = 255 - d[i];
		d[i+1] = 255 - d[i + 1];
		d[i+2] = 255 - d[i + 2];
	}

	return pixels;

};
Copy the code

A simple chestnut

Vue animation widget

<template>
  <canvas ref="bubble" :width="width" :height="height" :style="style"></canvas>
</template>

<script type="text/ecmascript-6">
  export default {
    props: {
      y: {
        type: Number.default: 0
      }
    },
    data() {
      return {
        width: 50.height: 80}},computed: {
      distance() {
        return Math.max(0.Math.min(this.y * this.ratio, this.maxDistance))
      },
      style() {
        return `width:The ${this.width / this.ratio}px; height:${this.height / this.ratio}px`
      }
    },
    created() {
      this.ratio = window.devicePixelRatio
      this.width *= this.ratio
      this.height *= this.ratio
      this.initRadius = 18 * this.ratio
      this.minHeadRadius = 12 * this.ratio
      this.minTailRadius = 5 * this.ratio
      this.initArrowRadius = 10 * this.ratio
      this.minArrowRadius = 6 * this.ratio
      this.arrowWidth = 3 * this.ratio
      this.maxDistance = 40 * this.ratio
      this.initCenterX = 25 * this.ratio
      this.initCenterY = 25 * this.ratio
      this.headCenter = {
        x: this.initCenterX,
        y: this.initCenterY
      }
    },
    mounted() {
      this._draw()
    },
    methods: {
      _draw() {
        const bubble = this.$refs.bubble
        let ctx = bubble.getContext('2d')
        ctx.clearRect(0.0, bubble.width, bubble.height)

        this._drawBubble(ctx)

        this._drawArrow(ctx)
      },
      _drawBubble(ctx) {
        ctx.save()
        ctx.beginPath()

        const rate = this.distance / this.maxDistance
        const headRadius = this.initRadius - (this.initRadius - this.minHeadRadius) * rate

        this.headCenter.y = this.initCenterY - (this.initRadius - this.minHeadRadius) * rate

        // Draw a half arc
        ctx.arc(this.headCenter.x, this.headCenter.y, headRadius, 0.Math.PI, true)

        // Draw bezier on the left
        const tailRadius = this.initRadius - (this.initRadius - this.minTailRadius) * rate
        const tailCenter = {
          x: this.headCenter.x,
          y: this.headCenter.y + this.distance
        }

        const tailPointL = {
          x: tailCenter.x - tailRadius,
          y: tailCenter.y
        }
        const controlPointL = {
          x: tailPointL.x,
          y: tailPointL.y - this.distance / 2
        }

        ctx.quadraticCurveTo(controlPointL.x, controlPointL.y, tailPointL.x, tailPointL.y)

        // Draw a half arc
        ctx.arc(tailCenter.x, tailCenter.y, tailRadius, Math.PI, 0.true)

        // Draw bezier on the right
        const headPointR = {
          x: this.headCenter.x + headRadius,
          y: this.headCenter.y
        }
        const controlPointR = {
          x: tailCenter.x + tailRadius,
          y: headPointR.y + this.distance / 2
        }
        ctx.quadraticCurveTo(controlPointR.x, controlPointR.y, headPointR.x, headPointR.y)

        ctx.fillStyle = 'RGB (170170170).
        ctx.fill()
        ctx.strokeStyle = 'RGB (153153153).
        ctx.stroke()
        ctx.restore()
      },
      _drawArrow(ctx) {
        ctx.save()
        ctx.beginPath()

        const rate = this.distance / this.maxDistance
        const arrowRadius = this.initArrowRadius - (this.initArrowRadius - this.minArrowRadius) * rate

        / / draw inside the circle
        ctx.arc(this.headCenter.x, this.headCenter.y, arrowRadius - (this.arrowWidth - rate), -Math.PI / 2.0.true)

        / / all round
        ctx.arc(this.headCenter.x, this.headCenter.y, arrowRadius, 0.Math.PI * 3 / 2.false)

        ctx.lineTo(this.headCenter.x, this.headCenter.y - arrowRadius - this.arrowWidth / 2 + rate)
        ctx.lineTo(this.headCenter.x + this.arrowWidth * 2 - rate * 2.this.headCenter.y - arrowRadius + this.arrowWidth / 2)

        ctx.lineTo(this.headCenter.x, this.headCenter.y - arrowRadius + this.arrowWidth * 3 / 2 - rate)

        ctx.fillStyle = 'RGB (255255255).
        ctx.fill()
        ctx.strokeStyle = 'RGB (170170170).
        ctx.stroke()
        ctx.restore()
      }
    },
    watch: {
      y() {
        this._draw()
      }
    }
  }
</script>

<style scoped>

</style>
Copy the code