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 };
}
|