背景
React(TypeScript)でcanvas要素を扱いたいと思ったところ、
Reactの場合だとどんなふうにコードを書けばいいか分からず、
色々と苦戦したので備忘録として残します。
ゴール
canvas上で図形をドラックして動かせるようなサンプルを作ります。
結論
このように書くと、canvas上で赤い四角形をドラックして動かせるサンプルができます。
import React, { useEffect, useRef, FC, useState } from "react";
export const Canvas:FC = () =>{
const canvasRef = useRef<HTMLCanvasElement>(null);//canvas要素取得
const [x,setX] = useState<number>(0);//X座砲
const [y,setY] = useState<number>(0);//Y座標
const [drug,SetDrug] = useState(false);//マウスドラック状態の管理
useEffect(() => {
if (!canvasRef.current) {
throw new Error("canvas要素の取得に失敗しました");
}
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
if (!ctx) {
throw new Error("context取得失敗");
}
//描画前に既に描画済みのものを消してリセット
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
//赤色の四角形を描画
ctx.fillStyle = "red";
ctx.fillRect(x, y, 10, 10);
}, [x,y,drug]);
const StartMove =()=>{
SetDrug(true);
}
const EndMove = () => {
SetDrug(false);
};
const Move = (e:any) => {
//変数drugがtrueの場合座標を更新する
if(drug){
const canvas:any= canvasRef.current;
const rect = canvas.getBoundingClientRect();//キャンバスの位置取得
setX(e.clientX - rect.left);//マウスのX座標をセット
setY(e.clientY - rect.top);//マウスのY座標をセット
}
};
return(
<>
<div className="my-10" >
<div className="border border-slate-700 p-3">
<h1 className="text-center text-3xl">キャンバス</h1>
<h2>座標</h2>
<p>x軸:{x}</p>
<p>y軸:{y}</p>
<canvas ref={canvasRef}
width={400}
height={450}
onMouseDown={StartMove}
onMouseUp={EndMove}
onMouseMove={Move}
className="border border-blue-700"
/>
</div>
</div>
</>
);
}
ポイント
まずキャンバスを取得するには"useRef"で取得するのがいいようです。
canvasタグにidをつけてdeiElementByIdで取得もできますが、
状態管理する上ではこのやり方がいいみたいです。
const canvasRef = useRef<HTMLCanvasElement>(null);//canvas要素取得
また、キャンバスに描画する際はuseEffect内で実行するのがいいようです。
今回のサンプルでは、座標(x,y)や描画可能か判定する変数drugが更新される度にuseEffectでanvasの描画を再実行することで四角形の描画位置を変更しています。
useEffect(() => {
if (!canvasRef.current) {
throw new Error("canvas要素の取得に失敗しました");
}
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
if (!ctx) {
throw new Error("context取得失敗");
}
//描画前に既に描画済みのものを消してリセット
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
//赤色の四角形を描画
ctx.fillStyle = "red";
ctx.fillRect(x, y, 10, 10);
}, [x,y,drug]);