캔버스 및 2차원 배열을 사용한 반응 연습: 삼각형이 있는 배경

이것은 다음과 같은 작은 삼각형의 배경을 그리는 구성 요소를 만드는 React 연습입니다.

shift를 사용하면 오른쪽으로만 기울입니다.


시프트 없음, 오른쪽으로만 기울임:


Shift 키를 사용하여 오른쪽/왼쪽으로 기울이기:


이 연습에서는 다음 방법을 알아봅니다.
  • js에서 2차원 행렬을 만듭니다.
  • html 캔버스 요소로 삼각형 그리기,
  • React에서 캔버스를 표시합니다.

  • useRef, useEffect를 사용할 것입니다.

    또한 여기에서 선택 사항인 ts 및 styled-components도 사용합니다.

    코드 내부의 주석은 충분한 설명을 제공해야 합니다.

    From MDN Web Docs: js에서 함수는 객체이며 다른 객체와 마찬가지로 속성과 메서드를 가질 수 있습니다. 단, 함수를 호출할 수 있습니다.

    1단계: React 프로젝트 내에서 "matrix.ts" 파일을 만듭니다.




    // ------------------------------------
    // This provides a 2 dimensional array.
    // Dimensions are x & y.
    // Each cell is a string.
    // It is implemented as a function object.
    // Prototype functions: setValue, getValue.
    function Matrix() {
      this.getValue = (x: number, y: number): string => this[`${x}:${y}`];
    
      this.setValue = (x: number, y: number, value: string) => {
        this[`${x}:${y}`] = value;
      };
    }
    
    export default Matrix;
    


    2단계: 새 구성 요소 파일 "BackgroundWithTriangles.tsx" 만들기




    import React, { useRef, useEffect } from "react";
    import styled from "styled-components";
    
    // @ts-ignore
    import Matrix from "misc/matrix";
    
    // SETTINGS:
    // Canvas contains many squares, each square contains 2 triangles.
    // l=logical, w=width, h=height.
    const canvas_lw = 1000; // higher for less pixelation
    const canvas_lh = 1000; // higher for less pixelation
    const square_lw = 30;
    const square_lh = 30;
    const squareShift_lx = 4; // horizontal
    const squareShift_ly = 4; // vertical
    const tilt = 0.5; // 0=left, 0.5=equal, 1=right
    const drawSquaresOnly = false;
    
    // THESE THREE MUST ADD UP TO 256, FOR RGB:
    const grayMinimum = 180; // higher for lighter.
    const colourShift = 3; // 0 for full grayscale.
    const grayShift = 256 - grayMinimum - colourShift; // 0+
    
    // ------------------------------------
    const Canvas = styled.canvas`
      position: fixed;
      z-index: -1;
      top: 0;
      left: 0;
      width: calc(100vw + 50px); // 50px: compensate for the shifting at the end
      height: calc(100vh + 50px); // 50px: compensate for the shifting at the end
    `;
    
    // ------------------------------------
    // Output range: 0 .. maxIncl.
    const getRandomInt = (maxIncl: number) =>
      Math.floor(Math.random() * (maxIncl + 1));
    
    // Output range: -x/2 .. x/2
    const getShiftPositiveOrNegative = (x: number) => getRandomInt(x) - x / 2;
    
    // ------------------------------------
    const getRandomGrayishRgb = () => {
      const randomGrayBase = grayMinimum + getRandomInt(grayShift);
      const r = randomGrayBase + getRandomInt(colourShift);
      const g = randomGrayBase + getRandomInt(colourShift);
      const b = randomGrayBase + getRandomInt(colourShift);
      return `rgb(${r},${g},${b})`;
    };
    
    // ------------------------------------
    // "12:34" --> [12, 34]
    const stringToArray = (value: string): number[] =>
      value.split(":").map((s: string) => Number(s));
    
    // [12, 34] --> "12:34"
    const arrayToString = (valueX: number, valueY: number): string =>
      `${valueX}:${valueY}`;
    
    // ------------------------------------
    const drawTriangle = (
      ctx: any,
      x1: number, y1: number,
      x2: number, y2: number,
      x3: number, y3: number,
      fillStyle: string
    ) => {
      ctx.beginPath();
      ctx.lineWidth = 0;
      ctx.fillStyle = fillStyle;
      ctx.moveTo(x1, y1);
      ctx.lineTo(x2, y2);
      ctx.lineTo(x3, y3);
      ctx.fill();
    };
    
    // ------------------------------------
    const drawSquare = (
      ctx: any,
      x1: number, y1: number,
      x2: number, y2: number,
      x3: number, y3: number,
      x4: number, y4: number,
      fillStyle: string
    ) => {
      ctx.beginPath();
      ctx.lineWidth = 0;
      ctx.fillStyle = fillStyle;
      ctx.moveTo(x1, y1);
      ctx.lineTo(x2, y2);
      ctx.lineTo(x4, y4);
      ctx.lineTo(x3, y3);
      ctx.closePath();
      ctx.fill();
    };
    
    // ------------------------------------
    // Two triangles forming a square.
    const drawSquareOrTriangles = (
      ctx: any,
      x1: number, y1: number,
      x2: number, y2: number,
      x3: number, y3: number,
      x4: number, y4: number
    ) => {
      if (drawSquaresOnly) {
        drawSquare(ctx, x1, y1, x2, y2, x3, y3, x4, y4, getRandomGrayishRgb());
        return;
      }
    
      // Draw two triangles
      if (Math.random() <= tilt) {
        // Tilt right, like: /
        drawTriangle(ctx, x1, y1, x2, y2, x3, y3, getRandomGrayishRgb());
        drawTriangle(ctx, x2, y2, x3, y3, x4, y4, getRandomGrayishRgb());
      } else {
        // Tilt left, like: \
        drawTriangle(ctx, x1, y1, x2, y2, x4, y4, getRandomGrayishRgb());
        drawTriangle(ctx, x1, y1, x3, y3, x4, y4, getRandomGrayishRgb());
      }
    };
    
    // ------------------------------------
    // x, y: top left corner of the cell, which contain 1 square or 2 triangles.
    const drawCell = (matrix: any, ctx: any, x: number, y: number) => {
      // 4 corners of the square
      const x1 = x;
      const y1 = y;
      const x2 = x;
      const y2 = y + square_lh;
      const x3 = x + square_lw;
      const y3 = y;
      const x4 = x + square_lw;
      const y4 = y + square_lh;
    
      drawSquareOrTriangles(
        ctx,
        // @ts-ignore
        ...stringToArray(matrix.getValue(x1, y1)),
        ...stringToArray(matrix.getValue(x2, y2)),
        ...stringToArray(matrix.getValue(x3, y3)),
        ...stringToArray(matrix.getValue(x4, y4))
      );
    };
    
    // ------------------------------------
    const createMatrix = (ctx: any) => {
      const matrix = new Matrix();
    
      // Create a matrix of dots for the squares, with shifts
      for (let x = 0; x <= canvas_lw; x += square_lw)
        for (let y = 0; y <= canvas_lh; y += square_lh) {
          const xWithShift = x + getShiftPositiveOrNegative(squareShift_lx);
          const yWithShift = y + getShiftPositiveOrNegative(squareShift_ly);
          matrix.setValue(x, y, arrayToString(xWithShift, yWithShift));
        }
    
      // Draw the squares (we need 4 dots for each square)
      for (let x = 0; x <= canvas_lw - square_lw; x += square_lw)
        for (let y = 0; y <= canvas_lh - square_lh; y += square_lh) {
          drawCell(matrix, ctx, x, y);
        }
    };
    
    // ------------------------------------
    // COMPONENT:
    // Draws a window background of squares.
    // Each square draws 2 triangles.
    // Each triangle has random shifts in: corner positions, and colour.
    const BackgroundWithTriangles = () => {
      const ref = useRef(null);
    
      // ------------------------------------
      useEffect(() => {
        if (ref && ref.current) {
          const canvas: any = ref.current;
          const ctx = canvas.getContext("2d");
          createMatrix(ctx);
        }
      }, [ref?.current]);
    
      // ------------------------------------
      // Width and height: logical (int), not physical (px).
      return <Canvas ref={ref} width={canvas_lw} height={canvas_lh} />;
    };
    
    export default BackgroundWithTriangles;
    


    3단계: 다음 예와 같이 구성 요소(아마도 페이지) 내부에서 새 구성 요소를 사용합니다.




    import { BackgroundWithTriangles } from "components";
    
    ...
    return (
      <Page title="Outgoing trend">
        <BackgroundWithTriangles />
    
        <TrendChart>
    ...
    


    질문이나 제안을 환영합니다.

    좋은 웹페이지 즐겨찾기