17
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

React で Canvas を使ってお絵描きツールを作る

Posted at

概要

お絵かきツールを作るための最小の構成を作ったみた。
React + typescript を想定している。

成果物

canvas.gif

対象者

React で Canvas を使って最小限のお絵かきツールを作りたい方

動作保証バージョン

package.json

{
  "dependencies" {
    "@types/react": "^17.0.3",
    "@types/react-dom": "^17.0.3",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  }
}

ブラウザ

Chrome 90.0.4430.85

サンプルコード

このコードのいいところは余分なものを割と排除しているのでお好みで実装を追加できること。
機能不足の場合は所望の機能を追加くださいませ。

Canvas コンポーネント

import React, { useRef } from 'react';

interface IProps {
  width: number;
  height: number;
}

interface IRect {
  width: number;
  height: number;
  left: number;
  right: number;
  top: number;
  bottom: number;
}

const Canvas: React.FC<IProps> = (props) => {
  const { width, height } = props
  let canvasRef = useRef<HTMLCanvasElement | null>(null);
  let mouseX: number | null = null;
  let mouseY: number | null = null;

  const getContext = (): CanvasRenderingContext2D => {
    const canvas: any = canvasRef.current;
    return canvas.getContext('2d');
  };

  const OnClick = (e: React.MouseEvent<HTMLCanvasElement>) => {
    if (e.button !== 0) { return; }
    const canvas: any = canvasRef.current;
    const rect: IRect = canvas.getBoundingClientRect();
    const x = ~~(e.clientX - rect.left);
    const y = ~~(e.clientY - rect.top);
    Draw(x, y);
  }

  const OnMove = (e: React.MouseEvent<HTMLCanvasElement>) => {
    if (e.buttons !== 1) { return; }
    const canvas: any = canvasRef.current;
    const rect: IRect = canvas.getBoundingClientRect();
    const x = ~~(e.clientX - rect.left);
    const y = ~~(e.clientY - rect.top);
    Draw(x, y);
  }

  const DrawEnd = (e: React.MouseEvent<HTMLCanvasElement>) => {
    mouseX = null;
    mouseY = null;
  }

  const Draw = (x: number, y: number) => {
    const ctx = getContext();
    ctx.beginPath();
    ctx.globalAlpha = 1.0;
    if (mouseX === null || mouseY === null) {
      ctx.moveTo(x, y);
    } else {
      ctx.moveTo(mouseX, mouseY);
    }
    ctx.lineTo(x, y);
    ctx.lineCap = "round";
    ctx.lineWidth = 10;
    ctx.strokeStyle= "#000000";
    ctx.stroke();
    mouseX = x;
    mouseY = y;
  }

  const Reset = () => {
    const ctx = getContext();
    ctx.clearRect(0, 0, width, height);
  }

  return (
    <section>
      <div>
        <canvas onMouseDown={OnClick}
                onMouseMove={OnMove}
                onMouseUp={DrawEnd}
                onMouseOut={DrawEnd}
                ref={canvasRef}
                width={`${width}px`}
                height={`${height}px`}
        />
      </div>
      <div>
        <button onClick={Reset}>リセット</button>
      </div>
    </section>
  );
}

export default Canvas;

呼び出す側

import './Canvas';

const App = () => {
  return <Canvas width={800} height={600} />
}

余談

個人的には謎解きキットの一部として実装したものなので、 canvas に background-image の css を付与して以下のように利用している。

drawing-canvas.gif

ツイート

この記事で一人でも多くのエンジニアが車輪の再発明をすることなく、手早くやりたいことを実現できますように🐳

17
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?