파일 구성

이미지 불러오기

// canvasDrawer.ts 

import { loadImage } from "../utils/image.ts";

class CanvasDrawer {
    private canvas: HTMLCanvasElement;
    private ctx: CanvasRenderingContext2D | null;
    private proceedImage: HTMLImageElement | null = null;
    private finishImage: HTMLImageElement | null = null;

    constructor(
        canvas: HTMLCanvasElement,
        proceedImagePath: string,
        finishImagePath: string,
    ) {
        this.canvas = canvas;
        this.ctx = this.canvas.getContext('2d');
        this.initializeCanvas(proceedImagePath, finishImagePath);
    }

    // 캔버스 초기화 (이미지 불러와서 그리기)
    private async initializeCanvas(proceedImagePath: string, finishImagePath: string) {
        const [proceedImage, finishImage] = await Promise.all([
            loadImage(proceedImagePath), loadImage(finishImagePath)
        ])
        this.proceedImage = proceedImage;
        this.finishImage = finishImage;
        this.ctx?.drawImage(this.proceedImage, 0, 0);
    }
}

export default CanvasDrawer;
// DrawerLineCanvas.tsx

import {FC, useEffect, useRef} from "react";
import CanvasDrawer from "../classes/canvasDrawer.ts";
import "./drawLineCanvas.css";

interface Props {
    drawingImage: string;
    completedImage: string;
}

const DrawLineCanvas: FC<Props> = ({drawingImage, completedImage}) => {
    const canvasRef = useRef<HTMLCanvasElement>(null);
    const drawerRef = useRef<CanvasDrawer | null>(null);

    useEffect(() => {
        if (canvasRef.current) drawerRef.current = new CanvasDrawer(
            canvasRef.current,
            drawingImage,
            completedImage,
        );
    }, []);

    return (
        <div className={"canvas_container"}>
            <canvas ref={canvasRef} width={600} height={400}/>
        </div>
    );
};

export default DrawLineCanvas;
// Ex1.tsx

import DrawLineCanvas from "./DrawLineCanvas.tsx";

// 비행기 선 그리는 예제
const Ex1 = () => {
    return (
        <DrawLineCanvas
            drawingImage={"./src/assets/dottodot_airplane.png"}
            completedImage={"./src/assets/dottodot_airplane_finish.png"}
        />
    );
};

export default Ex1;

image.png

실행하면 위와 같이 선 잇기 게임을 하기 위한 이미지가 뜨게 된다.

이미지 위에 선 그리기

// canvasDrawer.ts

class CanvasDrawer {
    ...
    // 시작한 점의 좌표
    private startX: number = 0;
    private startY: number = 0;
    // 몇 번째 점을 클릭했는지 추적하는 순서 카운터
    private order: number = 1;
    
    ...
    
    // 점 클릭
    public handleClick(event: React.MouseEvent<HTMLCanvasElement>) {
        const { offsetX: mouseX, offsetY: mouseY } = event.nativeEvent;

        if (this.order === 1) {
            this.startLine(mouseX, mouseY);
        } else {
            this.drawLine(mouseX, mouseY);
        }

        this.order++;
    }

    // 맨 처음 점의 좌표 찍기
    private startLine(mouseX: number, mouseY: number) {
        this.startX = mouseX - 5;
        this.startY = mouseY;
        this.ctx?.beginPath();
        this.ctx?.moveTo(this.startX, this.startY);
    }

    // 찍은 좌표까지 선 그리기
    private drawLine(mouseX: number, mouseY: number) {
        this.ctx?.lineTo(mouseX - 5, mouseY);
        this.ctx?.stroke();
    }
}

export default CanvasDrawer;