From 646cac2614824c6a81909f189b451d23f3d16289 Mon Sep 17 00:00:00 2001 From: "D.Joung" <121555686+AeuJoung@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:44:50 +0900 Subject: [PATCH] feat: Change drawing of paint tool coordinates received from socket (#187) * feat: Modification of maximum pixel amount setting * feat: Modification of maximum pixel amount * feat: Add applyFill method * feat: Add logic to distinguish paint tool and pen tool --- client/src/components/canvas/GameCanvas.tsx | 4 +-- client/src/components/quiz/QuizStage.tsx | 3 +- client/src/constants/canvasConstants.ts | 6 ++-- client/src/hooks/canvas/useDrawing.ts | 5 ++-- .../src/hooks/canvas/useDrawingOperation.ts | 28 +++++++++++++++++-- 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/client/src/components/canvas/GameCanvas.tsx b/client/src/components/canvas/GameCanvas.tsx index 77cde954c..e19b57de5 100644 --- a/client/src/components/canvas/GameCanvas.tsx +++ b/client/src/components/canvas/GameCanvas.tsx @@ -1,7 +1,7 @@ import { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent, useCallback, useEffect, useRef } from 'react'; import { PlayerRole, RoomStatus } from '@troublepainter/core'; import { Canvas } from '@/components/canvas/CanvasUI'; -import { COLORS_INFO, MAINCANVAS_RESOLUTION_WIDTH } from '@/constants/canvasConstants'; +import { COLORS_INFO, DEFAULT_MAX_PIXELS, MAINCANVAS_RESOLUTION_WIDTH } from '@/constants/canvasConstants'; import { drawingSocketHandlers } from '@/handlers/socket/drawingSocket.handler'; import { gameSocketHandlers } from '@/handlers/socket/gameSocket.handler'; import { useDrawing } from '@/hooks/canvas/useDrawing'; @@ -48,7 +48,7 @@ interface GameCanvasProps { * * @category Components */ -const GameCanvas = ({ role, maxPixels = 10000, currentRound, roomStatus, isHidden }: GameCanvasProps) => { +const GameCanvas = ({ role, maxPixels = DEFAULT_MAX_PIXELS, currentRound, roomStatus, isHidden }: GameCanvasProps) => { const canvasRef = useRef(null); const { convertCoordinate } = useCoordinateScale(MAINCANVAS_RESOLUTION_WIDTH, canvasRef); diff --git a/client/src/components/quiz/QuizStage.tsx b/client/src/components/quiz/QuizStage.tsx index 4cc6e931f..977a43d72 100644 --- a/client/src/components/quiz/QuizStage.tsx +++ b/client/src/components/quiz/QuizStage.tsx @@ -3,6 +3,7 @@ import { PlayerRole } from '@troublepainter/core'; import { GameCanvas } from '../canvas/GameCanvas'; import { QuizTitle } from '../ui/QuizTitle'; import sizzlingTimer from '@/assets/big-timer.gif'; +import { DEFAULT_MAX_PIXELS } from '@/constants/canvasConstants'; import { useTimer } from '@/hooks/useTimer'; import { useGameSocketStore } from '@/stores/socket/gameSocket.store'; import { cn } from '@/utils/cn'; @@ -56,7 +57,7 @@ const QuizGameContent = () => { currentRound={room.currentRound} roomStatus={room.status} role={roundAssignedRole || PlayerRole.GUESSER} - maxPixels={100000} + maxPixels={DEFAULT_MAX_PIXELS} isHidden={shouldHideCanvas} /> diff --git a/client/src/constants/canvasConstants.ts b/client/src/constants/canvasConstants.ts index 3b48f253b..e2ce55876 100644 --- a/client/src/constants/canvasConstants.ts +++ b/client/src/constants/canvasConstants.ts @@ -9,8 +9,8 @@ export const LINEWIDTH_VARIABLE = { STEP_WIDTH: 2, }; -export const MAINCANVAS_RESOLUTION_WIDTH = 1280; -export const MAINCANVAS_RESOLUTION_HEIGHT = 800; +export const MAINCANVAS_RESOLUTION_WIDTH = 1000; +export const MAINCANVAS_RESOLUTION_HEIGHT = 625; //해상도 비율 변경 시 CanvasUI의 aspect-[16/10] 도 수정해야 정상적으로 렌더링됩니다. export const COLORS_INFO = [ @@ -21,4 +21,4 @@ export const COLORS_INFO = [ { color: '회색', backgroundColor: '#808080' }, ]; -export const DEFAULT_MAX_PIXELS = 1000; // 기본값 설정 +export const DEFAULT_MAX_PIXELS = 50000; // 기본값 설정 diff --git a/client/src/hooks/canvas/useDrawing.ts b/client/src/hooks/canvas/useDrawing.ts index 1154ef324..6408ff16f 100644 --- a/client/src/hooks/canvas/useDrawing.ts +++ b/client/src/hooks/canvas/useDrawing.ts @@ -106,7 +106,7 @@ export const useDrawing = ( const strokeId = state.crdtRef.current.addStroke(drawingData); state.currentStrokeIdsRef.current.push(strokeId); - operation.drawStroke(drawingData); + if (state.drawingMode === DRAWING_MODE.PEN) operation.drawStroke(drawingData); return { type: CRDTMessageTypes.UPDATE, @@ -280,7 +280,8 @@ export const useDrawing = ( return; } - operation.drawStroke(stroke); + if (stroke.points.length > 2) operation.applyFill(stroke); + else operation.drawStroke(stroke); if (state.historyPointerRef.current < state.strokeHistoryRef.current.length - 1) { state.strokeHistoryRef.current = state.strokeHistoryRef.current.slice(0, state.historyPointerRef.current + 1); diff --git a/client/src/hooks/canvas/useDrawingOperation.ts b/client/src/hooks/canvas/useDrawingOperation.ts index b570aaeb5..c2c660cca 100644 --- a/client/src/hooks/canvas/useDrawingOperation.ts +++ b/client/src/hooks/canvas/useDrawingOperation.ts @@ -54,6 +54,7 @@ const checkColorisNotEqual = (pos: number, startColor: RGBA, pixelArray: Uint8Cl * @property getCurrentStyle - 현재 상태를 기반으로 스트로크 스타일을 반환하는 함수 * @property drawStroke - 캔버스에 단일 스트로크를 그리는 함수 * @property redrawCanvas - 저장된 스트로크들을 기반으로 전체 캔버스를 다시 그리는 함수 + * @property applyFill - 소켓에서 받아온 페인팅 좌표 배열을 그리는 함수 * @property floodFill - 지정된 좌표에서 영역 채우기를 수행하는 함수 * * @category Hooks @@ -104,9 +105,31 @@ export const useDrawingOperation = ( state.crdtRef.current.strokes .filter((stroke) => stroke.stroke !== null) - .forEach(({ stroke }) => drawStroke(stroke)); + .forEach(({ stroke }) => { + if (stroke.points.length > 2) applyFill(stroke); + else drawStroke(stroke); + }); }, [drawStroke]); + const applyFill = (drawingData: DrawingData) => { + const { canvas, ctx } = getCanvasContext(canvasRef); + const { points, style } = drawingData; + + if (points.length === 0) return; + + const color = hexToRGBA(style.color); + + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const data = imageData.data; + + points.forEach(({ x, y }) => { + const pos = (y * canvas.width + x) * 4; + fillTargetColor(pos, color, data); + }); + + ctx.putImageData(imageData, 0, 0); + }; + const floodFill = useCallback( (startX: number, startY: number) => { const { canvas, ctx } = getCanvasContext(canvasRef); @@ -126,7 +149,7 @@ export const useDrawingOperation = ( let pixelCount = 0; const filledPoints: Point[] = []; - while (pixelsToCheck.length > 0 && pixelCount < inkRemaining) { + while (pixelsToCheck.length > 0 && pixelCount <= inkRemaining) { const [x, y] = pixelsToCheck.shift()!; const pos = (y * canvas.width + x) * 4; @@ -165,6 +188,7 @@ export const useDrawingOperation = ( getCurrentStyle, drawStroke, redrawCanvas, + applyFill, floodFill, clearCanvas, };