All files / src/entities/shape/hooks useShapeDraw.ts

0% Statements 0/57
0% Branches 0/36
0% Functions 0/4
0% Lines 0/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94                                                                                                                                                                                           
import React, { useCallback, useRef } from 'react';
import { useAppSelector } from '@/store/hooks';
import { toCanvasPoint } from '@/shared/lib/utils';
import { SHAPES } from '@/shared/constants';
import type { MaybePoint } from '@/shared/types';
import { selectShapeState } from '@/entities/shape/model';
 
export function useShapeDraw(canvasRef: React.RefObject<HTMLCanvasElement | null>) {
	const { type, fill, stroke, thickness } = useAppSelector(selectShapeState);
 
	const startRef = useRef<MaybePoint>(null);
	const snapshot = useRef<ImageData | null>(null);
	const dpr = 1;
 
	const onPointerDown = useCallback(
		(pointerEvent: React.PointerEvent<HTMLCanvasElement>) => {
			const canvas = canvasRef.current;
			if (!canvas) return;
 
			const context = canvas.getContext('2d', { willReadFrequently: true });
			if (!context) return;
 
			startRef.current = toCanvasPoint(pointerEvent, canvas, { dpr });
 
			snapshot.current = context.getImageData(0, 0, canvas.width, canvas.height);
		},
		[canvasRef],
	);
 
	const onPointerMove = useCallback(
		(pointerEvent: React.PointerEvent<HTMLCanvasElement>) => {
			if (!canvasRef.current || !snapshot.current || !startRef.current) return;
			const canvas = canvasRef.current;
			if (!canvas || !snapshot.current || !startRef.current) return;
 
			const context = canvas.getContext('2d', { willReadFrequently: true });
			if (!context) return;
 
			context.putImageData(snapshot.current, 0, 0);
 
			const start = startRef.current;
			const end = toCanvasPoint(pointerEvent, canvas, { dpr });
 
			const x = Math.min(start.x, end.x);
			const y = Math.min(start.y, end.y);
			const w = Math.abs(start.x - end.x);
			const h = Math.abs(start.y - end.y);
 
			context.fillStyle = fill;
			context.strokeStyle = stroke;
			context.lineWidth = thickness;
 
			context.beginPath();
 
			if (type === SHAPES.RECT) {
				context.rect(x, y, w, h);
			} else if (type === SHAPES.CIRCLE) {
				const radius = Math.sqrt(w * w + h * h) / 2;
				const cx = (start.x + end.x) / 2;
				const cy = (start.y + end.y) / 2;
				context.arc(cx, cy, radius, 0, Math.PI * 2);
			}
 
			if (fill) {
				context.fill();
			}
			if (stroke && thickness > 0) {
				context.stroke();
			}
		},
		[canvasRef, fill, stroke, thickness, type],
	);
 
	const onPointerUp = useCallback(
		(pointerEvent: React.PointerEvent<HTMLCanvasElement>) => {
			if (!canvasRef.current || !startRef.current) return;
 
			const canvas = canvasRef.current;
			if (!canvas || !startRef.current) return;
 
			const context = canvas.getContext('2d', { willReadFrequently: true });
			if (!context) return;
 
			onPointerMove(pointerEvent);
 
			snapshot.current = null;
			startRef.current = null;
		},
		[canvasRef, onPointerMove],
	);
 
	return { onPointerDown, onPointerMove, onPointerUp };
}