メインコンテンツまでスキップ

DrawingCanvas

挙動

CameraCrop

使用ライブラリ

ソースコード

ソースコード
'use client'

import { useEffect, useLayoutEffect, useRef, useState } from 'react'

const DrawingCanvasPage: React.FC = () => {
const [width, setWidth] = useState(0)
const [height, setHeight] = useState(0)

// 初期描画前に画面サイズを取得
useLayoutEffect(() => {
setWidth(window.innerWidth)
setHeight(window.innerHeight)
}, [])

let canvasRef = useRef<HTMLCanvasElement | null>(null)
let mouseX: number | null = null
let mouseY: number | null = null

const preventPullToRefresh = (e: any) => {
if (e.touches.length !== 1) return
e.preventDefault()
}

useEffect(() => {
const canvasElement = canvasRef.current
canvasElement?.addEventListener('touchstart', preventPullToRefresh, { passive: false })
canvasElement?.addEventListener('touchmove', preventPullToRefresh, { passive: false })

return () => {
canvasElement?.removeEventListener('touchstart', preventPullToRefresh)
canvasElement?.removeEventListener('touchmove', preventPullToRefresh)
}
}, [])

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

const handleStart = (x: number, y: number) => {
Draw(x, y)
}

const handleMove = (x: number, y: number) => {
Draw(x, y)
}

// スマホクリック時の処理
const OnTouchStart = (e: React.TouchEvent<HTMLCanvasElement>) => {
const touch = e.touches[0] // 最初のタッチの座標を取得
const canvas: any = canvasRef.current
const rect = canvas.getBoundingClientRect()
const x = ~~(touch.clientX - rect.left)
const y = ~~(touch.clientY - rect.top)
handleStart(x, y)
}

// スマホ移動時の処理
const OnTouchMove = (e: React.TouchEvent<HTMLCanvasElement>) => {
const touch = e.touches[0]
const canvas: any = canvasRef.current
const rect = canvas.getBoundingClientRect()
const x = ~~(touch.clientX - rect.left)
const y = ~~(touch.clientY - rect.top)
handleMove(x, y)
}

// マウスクリック時の処理
const OnMouseDown = (e: React.MouseEvent<HTMLCanvasElement, MouseEvent>) => {
const canvas: any = canvasRef.current
const rect = canvas.getBoundingClientRect()
const x = ~~(e.clientX - rect.left)
const y = ~~(e.clientY - rect.top)
handleStart(x, y)
}

// マウス移動時の処理
const OnMouseMove = (e: React.MouseEvent<HTMLCanvasElement, MouseEvent>) => {
if (e.buttons !== 1) return
const canvas: any = canvasRef.current
const rect = canvas.getBoundingClientRect()
const x = ~~(e.clientX - rect.left)
const y = ~~(e.clientY - rect.top)
handleMove(x, y)
}

// 描画処理
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 DrawEnd = () => {
mouseX = null
mouseY = null
}

// リセット処理
const Reset = () => {
const ctx = getContext()
ctx.clearRect(0, 0, width, height)
}

return (
<section>
<button className='mt-1 ml-1 absolute shadow-lg p-2' onClick={Reset}>
リセット
</button>
<canvas
id='canvasId'
// スマホ用イベント
onTouchStart={OnTouchStart}
onTouchMove={OnTouchMove}
onTouchEnd={DrawEnd}
onTouchCancel={DrawEnd}
// PC用イベント
onMouseDown={OnMouseDown}
onMouseMove={OnMouseMove}
onMouseUp={DrawEnd}
onMouseLeave={DrawEnd}
ref={canvasRef}
width={width}
height={height}
/>
</section>
)
}

export default DrawingCanvasPage

検証ページ

こちら