Through Canvas nanny tutorial (PART 1) : Drawing, we know that all drawing actions on Canvas are completed by Javascript manipulation, and it is this feature that enables Canvas to achieve simple interactive animation.

Perhaps the biggest limitation of using Canvas for animation is that once the image is drawn, you have to redraw everything (including the previous ones) to move it. Redrawing is time-consuming, and performance depends on the speed of your computer.

The basic steps of animation

The steps for drawing each frame:

  • Clear canvas: for exampleclearRectmethods
  • Save the Canvas state:save()
  • Draw animation frames
  • Restore canvas state:restore()

Manipulation of the animation

To animate, we need some way to redraw on a regular basis.

  • setInterval(function, delay): Suitable for scenarios where no interaction is required
  • setTimeout(function, delay): Capture user interaction via keyboard or mouse events and reusesetTimeoutPerform the corresponding action
  • requestAnimationFrame(callback)This method is smoother and more efficient, calling the draw animation frame only when the system is ready to redraw conditions.

The long tail effect

The long tail effect can be achieved by covering the entire screen with a transparent fill rectangle when the canvas is cleared, such as this ball:

ctx.fillStyle = "Rgba (255255255,0.3)";
ctx.fillRect(0.0, canvas.width, canvas.height);
Copy the code

ImageData object

We can do pixel-level operations with ImageData objects.

The ImageData object stores the real pixel data of the Canvas object, which contains the following read-only properties:

  • width: Image width, in pixels
  • height: Image height, in pixels
  • data: Uint8ClampedArray one-dimensional array of type1, each four values represents a pixel, in the order of red, green, blue, and transparent values.

createImageData

There are two ways to create a new ImageData object:

  • For a specific size, all pixels are preset to transparent black:
var myImageData = ctx.createImageData(width, height);
Copy the code
  • Based on another ImageData object, instead of copying, the pixels of the new object will all be preset to transparent black:
var myImageData = ctx.createImageData(anotherImageData);
Copy the code

getImageData

Get pixel data:

var myImageData = ctx.getImageData(left, top, width, height);
Copy the code

Any element outside the canvas is returned as a transparent black ImageData object.

Pick color

With mouse time mousemove and Mousedown, we get the current pixel information based on mouse position and getImageData.

// Load the image
var img = new Image();
img.crossOrigin = "anonymous";
img.src =
  "https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3747c10c114f491daf72aa2b7e971c42~tplv-k3u1fbpfcp-zoom-crop-mark:1304: 1304:1304:734.awebp?";
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
img.onload = function () {
  ctx.drawImage(img, 0.0);
  img.style.display = "none";
};

// Color display block
var hoveredColor = document.getElementById("hovered-color");
var selectedColor = document.getElementById("selected-color");

// Get pixel data
function pick(event, target) {
  let pixel = ctx.getImageData(event.offsetX, event.offsetY, 1.1).data;
  let rgba = `rgba(${pixel[0]}.${pixel[1]}.${pixel[2]}.${pixel[3] / 255}) `;
  target.style.background = rgba;
  target.textContent = rgba;
}

// mousemove
canvas.addEventListener("mousemove".function (event) {
  pick(event, hoveredColor);
});

// mousedown
canvas.addEventListener("mousedown".function (event) {
  pick(event, selectedColor);
});
Copy the code

putImageData

Rewrite pixel information:

ctx.putImageData(myImageData, dx, dy);
Copy the code

Dx,dy is where you want to draw the upper left corner of the image.

Reverse phase color

Let’s make an inverse change to the image above:

var invert = function () {
  ctx.drawImage(img, 0.0);
  const imageData = ctx.getImageData(0.0, canvas.width, canvas.height);
  const data = imageData.data;
  for (var i = 0; i < data.length; i += 4) {
    data[i] = 255 - data[i]; // red
    data[i + 1] = 255 - data[i + 1]; // green
    data[i + 2] = 255 - data[i + 2]; // blue
  }
  ctx.putImageData(imageData, 0.0);
};
invert();
Copy the code

Save the picture

canvas.toDataURL("image/png");
Copy the code

A PNG image is created by default.

We can also choose to create a JPEG image, and we can also choose quality from 0 to 1, with 1 being the best quality and 0 being largely undiscriminated but having a smaller file size.

canvas.toDataURL("image/jpeg", quality);
Copy the code

The optimization of Canvas

If you are using Canvas for games and complex visualizations, these performance improvements will be helpful:

  • Avoid floating point coordinates and replace them with integers
    / / ❌
    ctx.drawImage(myImage, 0.3.0.5);
    / / ✅
    ctx.drawIamge(myImage, Math.floor(0.3), Math.floor(0.5));
    Copy the code
  • Do not scale the image with drawImage. You can cache different sizes of the image
  • Use multiple layers of canvas to paint a complex scene, such as a static background and frequently moving objects
  • Use CSS to set the large background image to avoid repeatedly drawing the large image on the canvas for each frame.
  • Scaling the canvas with CSS Transforms Features CSS Transforms are faster with gpus. It is certainly better not to scale the canvas directly, or to have small canvases scaled up rather than large canvases scaled down.
  • Turn off transparencyIf transparency is not required, the browser will be optimized internally after manual closing.
    var ctx = canvas.getContext("2d", { alpha: false });
    Copy the code
  • Group function calls to the canvas together (for example, draw one polyline instead of multiple separate lines)
  • Avoid unnecessary canvas state changes
  • Render different points in the canvas, not the entire new state
  • Avoid as much as possibleshadowBlurfeatures
  • Avoid as much as possibletext rendering
  • Different scenes can choose different ways to clear the canvas:clearRect(),fillRect(), adjust canvas size
  • usewindow.requestAnimationFrame()Rather thanwindow.setInterval()
  • Use large physical libraries with caution

  1. Uint8ClampedArrayA typed array represents an array of 8-bit unsigned integers whose values are fixed between 0 and 255; If you specify a value outside the range [0,255], it will be replaced with 0 or 255; If you specify a non-integer, it will be set to the nearest integer.↩