What is Canvas?

HTML5’s Canvas element uses JavaScript to draw images on web pages.

A canvas is a rectangular area that you can control every pixel of.

Canvas has a variety of ways to draw paths, rectangles, circles, characters and add images.

Canvas image manipulation

Canvas image manipulation is mainly the following API: drawImage, createImageData, getImageData, putImageData;

methods

describe

drawImage

Draw an image, canvas, or video on a canvas

createImageData

Create a new, blank ImageData object

getImageData

Returns an ImageData object that copies pixel data for the specified rectangle on the canvas

putImageData

Puts the ImageData (from the specified ImageData object) back onto the canvas

Based on the above, we can draw our image in the Canvas element, assuming our image is image.jpg

Img = new Image() // Declare a new Image object img.src = "./img/photo.jpg" const canvas = document.querySelector("#myCanvas"); // Get canvas element const CTX = canvas.getContext("2d"); Img. onload = function() {// Canvas = img.width; canvas.height = img.height; / / to draw the picture the canvas CTX drawImage (img, 0, 0, canvas width, canvas, height); }Copy the code

Now we have drawn the image to the canvas

Canvas filter

Here, we’re going to be dealing with imageData

The getImageData() method returns an ImageData object that copies the pixel data of the rectangle specified on the canvas.

For each pixel in the ImageData object, there is four-sided information, the RGBA value:

R – Red (0-255)

G – Green (0-255)

B – Blue (0-255)

A-alpha channel (0-255; 0 is transparent, 255 is fully visible)

Color /alpha exists as an array and is stored in the Data property of the ImageData object.

Let’s set up a basic project framework. Here I’m using create-React-app generated projects

import React, { useState, useLayoutEffect, useRef } from 'react'; function Home() { const [size, setSize] = useState({ width: 0, height: 0 }); const canvasRef = useRef(null); const imgRef = useRef(null); const imageLoad = () => { const width = imgRef.current.width; const height = imgRef.current.height; setSize({ width, height }); canvasRef.current.width = width; canvasRef.current.height = height; queryImageData(); }; useLayoutEffect(() => { const { width, height } = size; const ctx = canvasRef.current.getContext('2d'); After the completion of the loading / / pictures Draw the picture the canvas of CTX. DrawImage (imgRef. Current, 0, 0, imgRef. Current. NaturalWidth, imgRef. Current. NaturalHeight, 0, 0, width, height); }, [size]); return ( <div className='home-view'> <div className='home-content'> <div className='content'> <img onLoad={imageLoad} ref={imgRef} src={img} alt='little' /> </div> <div className='content'> <canvas ref={canvasRef} id='canvas' /> </div> </div> </div> ); } // CSS I will not paste basic no contentCopy the code

When the image is loaded, we put the image into the canvas, and we don’t do anything with the image;

The next step is to apply a filter to the image

Adding a filter to an image is to operate on the data in imageData, and then put the changed data back into the canvas for display

Next we add action buttons at the bottom of the page

import React, { useState, useLayoutEffect, useRef } from 'react'; import CanvasRect from '.. /.. /utils/draw'; import photoFilter from '.. /.. /utils/filter'; import img from '.. /.. /static/img/4a5e48e736d12f2ef27d70b84fc2d56284356824.jpg'; import './index.less'; Const buttonList = [{text: 'normal', code: 'normal'}, {text: 'gray ', code:' HDLJ '}, {text: 'black ', code: 'HBLJ'}, {text: 'reverse filter, code:' FXLJ}, {text: 'to a color filter, code:' QSLJ}, {text: 'monochromatic filter, code:' DSLJ}, {text: 'nostalgic filters' code:' HJLJ '}, {text: 'fused filter, code:' RZLJ}, {text: 'frozen filters, code:' BDLJ}, {text: 'comics filters, code: 'LHHLJ'}, {text: 'dark color filters, code:' adlj}, {text: 'a gaussian blur filter, code:' GSMMLJ},]; function Home() { const [size, setSize] = useState({ width: 0, height: 0 }); const canvasRef = useRef(null); const imgRef = useRef(null); const [type, setType] = useState(-1); const imageLoad = () => { const width = imgRef.current.width; const height = imgRef.current.height; setSize({ width, height }); canvasRef.current.width = width; canvasRef.current.height = height; queryImageData(); }; useLayoutEffect(() => { const { width, height } = size; const ctx = canvasRef.current.getContext('2d'); ctx.drawImage(imgRef.current, 0, 0, imgRef.current.naturalWidth, imgRef.current.naturalHeight, 0, 0, width, height); }, [size]); const frameCanvas = () => { const img = imgRef.current; const width = img.width; const height = img.height; const naturalWidth = img.naturalWidth; const naturalHeight = img.naturalHeight; let canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; let context = canvas.getContext('2d'); return [canvas, context, width, height, naturalWidth, naturalHeight]; }; const queryImageData = () => { const img = imgRef.current; const [, context, width, height, naturalWidth, naturalHeight] = frameCanvas(); context.drawImage(img, 0, 0, naturalWidth, naturalHeight, 0, 0, width, height); const imgData = context.getImageData(0, 0, width, height); return imgData; }; const change = async (type) => { const { width, height } = size; await setType(type); const func = photoFilter[type]; const baseImageData = queryImageData(); Const imageData = func? func(baseImageData) : baseImageData; Const img = filterImage(imageData); / / get the new picture const CTX = canvasRef. The current. The getContext (" 2 d "); new CanvasRect(img, ctx); // Place the image on canvas ctx.clearRect(0, 0, width, height); }; const filterImage = (imageData) => { const [canvas, context] = frameCanvas(); context.putImageData(imageData, 0, 0); const src = canvas.toDataURL(); return src; }; return ( <div className='home-view'> <div className='home-content'> <div className='content'> <img onLoad={imageLoad} ref={imgRef} src={img} alt='little' /> </div> <div className='content'> <canvas ref={canvasRef} id='canvas' /> </div> </div> <div className='home-footer'> {buttonList.map((v) => { return ( <div key={v.code} onClick={() => change(v.code)} className={`footer-button ${v.code === type ? 'active' : ''}`}> {v.text} </div> ); })} </div> </div> ); }Copy the code

PhotoFilter is just some algorithm for filters, CanvasRect is to put the image on the canvas

Gray filter

Average values of R, G and B for each pixel

function (imgData) {
    let data = imgData.data;
    for (var i = 0; i < data.length; i += 4) {
      var grey = (data[i] + data[i + 1] + data[i + 2]) / 3;
      data[i] = data[i + 1] = data[i + 2] = grey;
    }
    return imgData;
  }
Copy the code

Black and white filter

If the average value of R, G and B of each pixel is greater than 125, it is set to 255; otherwise, it is set to 0

function (imgData) { let data = imgData.data; for (var i = 0; i < data.length; i += 4) { var avg = (data[i] + data[i + 1] + data[i + 2]) / 3; data[i] = data[i + 1] = data[i + 2] = avg >= 125 ? 255:0; } return imgData; }Copy the code

Reverse filter

Invert R, G, and B for each pixel

function (imgData) {
    let data = imgData.data;
    for (var i = 0; i < data.length; i += 4) {
      data[i] = 255 - data[i];
      data[i + 1] = 255 - data[i + 1];
      data[i + 2] = 255 - data[i + 2];
    }
    return imgData;
  }
Copy the code

To color filter

Set R, G, and A to one half of the sum of the maximum and minimum values

function (imgData) {
    let data = imgData.data;
    for (let i = 0; i < data.length; i++) {
      let avg = Math.floor((Math.min(data[i], data[i + 1], data[i + 2]) + Math.max(data[i], data[i + 1], data[i + 2])) / 2);
      data[i] = data[i + 1] = data[i + 2] = avg;
    }
    return imgData;
  }
Copy the code

Monochromatic filter

Keep one of R, G, and B and set the other two to zero

function (imgData) {
    let data = imgData.data;
    for (let i = 0; i < data.length; i++) {
      data[i * 4 + 2] = 0;
      data[i * 4 + 1] = 0;
    }
    return imgData;
  }
Copy the code

Nostalgic filter

function (imgData) {
    let data = imgData.data;
    for (let i = 0; i < data.length; i++) {
      let r = data[i * 4],
        g = data[i * 4 + 1],
        b = data[i * 4 + 2];
      let newR = 0.393 * r + 0.769 * g + 0.189 * b;
      let newG = 0.349 * r + 0.686 * g + 0.168 * b;
      let newB = 0.272 * r + 0.534 * g + 0.131 * b;
      let rgbArr = [newR, newG, newB].map((e) => {
        return e < 0 ? 0 : e > 255 ? 255 : e;
      });
      [data[i * 4], data[i * 4 + 1], data[i * 4 + 2]] = rgbArr;
    }
    return imgData;
  }
Copy the code

Fused filter

function (imgData) {
    let data = imgData.data;
    for (let i = 0; i < data.length; i++) {
      let r = data[i * 4],
        g = data[i * 4 + 1],
        b = data[i * 4 + 2];
      let newR = (r * 128) / (g + b + 1);
      let newG = (g * 128) / (r + b + 1);
      let newB = (b * 128) / (g + r + 1);
      let rgbArr = [newR, newG, newB].map((e) => {
         return e < 0 ? e * -1 : e;
      });
      [data[i * 4], data[i * 4 + 1], data[i * 4 + 2]] = rgbArr;
    }
    return imgData;
  }
Copy the code

Freezing filter

function (imgData) {
    let data = imgData.data;
    for (let i = 0; i < data.length; i++) {
      let r = data[i * 4],
        g = data[i * 4 + 1],
        b = data[i * 4 + 2];
      let newR = ((r - g - b) * 3) / 2;
      let newG = ((g - r - b) * 3) / 2;
      let newB = ((b - g - r) * 3) / 2;
      let rgbArr = [newR, newG, newB].map((e) => {
        return e < 0 ? e * -1 : e;
      });
      [data[i * 4], data[i * 4 + 1], data[i * 4 + 2]] = rgbArr;
    }
    return imgData;
  }
Copy the code

Comic strip filter

 function (imgData) {
    let data = imgData.data;
    for (let i = 0; i < data.length; i++) {
      let r = data[i * 4],
        g = data[i * 4 + 1],
        b = data[i * 4 + 2];
      let newR = (Math.abs(g - b + g + r) * r) / 256;
      let newG = (Math.abs(b - g + b + r) * r) / 256;
      let newB = (Math.abs(b - g + b + r) * g) / 256;
      let rgbArr = [newR, newG, newB].map((e) => {
        return e < 0 ? 0 : e > 255 ? 255 : e;
      });
      [data[i * 4], data[i * 4 + 1], data[i * 4 + 2]] = rgbArr;
    }
    return imgData;
  }
Copy the code

Dark color filter

function (imgData) {
    let data = imgData.data;
    for (let i = 0; i < data.length; i++) {
      let r = data[i * 4],
        g = data[i * 4 + 1],
        b = data[i * 4 + 2];
      let newR = (r * r) / 255;
      let newG = (g * g) / 255;
      let newB = (b * b) / 255;
      let rgbArr = [newR, newG, newB].map((e) => {
        return e < 0 ? 0 : e > 255 ? 255 : e;
      });
      [data[i * 4], data[i * 4 + 1], data[i * 4 + 2]] = rgbArr;
    }
    return imgData;
  }
Copy the code

Gaussian blur filter

function (imgData, radius = 5, sigma = radius / 3) {
    let handleEdge = (i, x, w) => {
      var m = x + i;
      if (m < 0) {
        m = -m;
      } else if (m >= w) {
        m = w + i - x;
      }
      return m;
    };

    var pixes = imgData.data,
      height = imgData.height,
      width = imgData.width;
    var gaussEdge = radius * 2 + 1;
    var gaussMatrix = [],
      gaussSum = 0,
      a = 1 / (2 * sigma * sigma * Math.PI),
      b = -a * Math.PI;

    for (var i = -radius; i <= radius; i++) {
      for (var j = -radius; j <= radius; j++) {
        var gxy = a * Math.exp((i * i + j * j) * b);
        gaussMatrix.push(gxy);
        gaussSum += gxy;
      }
    }
    var gaussNum = (radius + 1) * (radius + 1);
    for (let i = 0; i < gaussNum; i++) {
      gaussMatrix[i] /= gaussSum;
    }

    for (let x = 0; x < width; x++) {
      for (let y = 0; y < height; y++) {
        let r = 0,
          g = 0,
          b = 0;
        for (let i = -radius; i <= radius; i++) {
          let m = handleEdge(i, x, width);
          for (let j = -radius; j <= radius; j++) {
            let mm = handleEdge(j, y, height);
            let currentPixId = (mm * width + m) * 4;
            let jj = j + radius;
            let ii = i + radius;
            r += pixes[currentPixId] * gaussMatrix[jj * gaussEdge + ii];
            g += pixes[currentPixId + 1] * gaussMatrix[jj * gaussEdge + ii];
            b += pixes[currentPixId + 2] * gaussMatrix[jj * gaussEdge + ii];
          }
        }
        let pixId = (y * width + x) * 4;
        pixes[pixId] = ~~r;
        pixes[pixId + 1] = ~~g;
        pixes[pixId + 2] = ~~b;
      }
    }
    return imgData;
  }
Copy the code

. That’s all for now, and I’ll add something later. This is the first post, and there are not too many words. I hope you don’t mind, and I hope you can learn something fun.

Refer to the link

Image filter effects (Reverse color, emboss, Sculpture, Nostalgic, Frozen, dark) (1)

Image filter effects (Exposure, Neon, comic strip, casting) (2)

The code address

canvas-filter

Webpack Loader’s little filter is useless

❤️ thank you