diff --git a/src/components/canvas/CanvasUI.tsx b/src/components/canvas/CanvasUI.tsx index ca9036e..e994c03 100644 --- a/src/components/canvas/CanvasUI.tsx +++ b/src/components/canvas/CanvasUI.tsx @@ -10,6 +10,8 @@ type CanvasUIProps = { onImageDelete: () => void; hasImage: boolean; colors: string[]; + onZoomIn: () => void; + onZoomOut: () => void; }; export default function CanvasUI(props: CanvasUIProps) { diff --git a/src/components/canvas/CanvasUIMobile.tsx b/src/components/canvas/CanvasUIMobile.tsx index 37bb78d..6f4c81f 100644 --- a/src/components/canvas/CanvasUIMobile.tsx +++ b/src/components/canvas/CanvasUIMobile.tsx @@ -17,6 +17,8 @@ type CanvasUIProps = { onImageDelete: () => void; hasImage: boolean; colors: string[]; + onZoomIn: () => void; + onZoomOut: () => void; }; export default function CanvasUIMobile({ @@ -26,6 +28,8 @@ export default function CanvasUIMobile({ onImageDelete, hasImage, colors, + onZoomIn, + onZoomOut, }: CanvasUIProps) { const [isPressed, setIsPressed] = useState(false); const [showConfirmEffect, setShowConfirmEffect] = useState(false); @@ -319,10 +323,52 @@ export default function CanvasUIMobile({ )} {/* 좌표 표시창 */} -
+
{hoverPos ? `(${hoverPos.x}, ${hoverPos.y})` : 'OutSide'}
+ {/* 확대/축소 버튼 */} +
+ + +
+ {/* 팔레트 */}
{ + const canvas = renderCanvasRef.current; + if (!canvas) return; + + const centerX = canvas.clientWidth / 2; + const centerY = canvas.clientHeight / 2; + + const xs = (centerX - viewPosRef.current.x) / scaleRef.current; + const ys = (centerY - viewPosRef.current.y) / scaleRef.current; + + const newScale = Math.max( + MIN_SCALE, + Math.min(MAX_SCALE, scaleRef.current * scaleChange) + ); + + scaleRef.current = newScale; + viewPosRef.current.x = centerX - xs * scaleRef.current; + viewPosRef.current.y = centerY - ys * scaleRef.current; + + draw(); + updateOverlay(centerX, centerY); + }, + [draw, updateOverlay] + ); + + const handleZoomIn = useCallback(() => { + zoomCanvas(1.2); + }, [zoomCanvas]); + + const handleZoomOut = useCallback(() => { + zoomCanvas(1 / 1.2); + }, [zoomCanvas]); + const handleCooltime = useCallback(() => { startCooldown(10); }, [startCooldown]); @@ -1070,6 +1104,8 @@ function PixelCanvas({ onImageAttach={handleImageAttach} onImageDelete={cancelImage} hasImage={!!imageCanvasRef.current} + onZoomIn={handleZoomIn} + onZoomOut={handleZoomOut} /> )} {showImageControls && !isImageFixed && ( diff --git a/src/components/toast/InstructionsToast.tsx b/src/components/toast/InstructionsToast.tsx index 9410fa1..c07777a 100644 --- a/src/components/toast/InstructionsToast.tsx +++ b/src/components/toast/InstructionsToast.tsx @@ -2,7 +2,9 @@ import { toast } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; export const showInstructionsToast = () => { - toast( + const isMobile = window.innerWidth < 768; // 모바일 기기 판단 기준 + + const mobileInstructions = (
How to Play
@@ -20,8 +22,8 @@ export const showInstructionsToast = () => {

- 마우스 좌클릭:{' '} - 픽셀 선택 + 픽셀 선택:{' '} + 탭 (클릭)

@@ -39,8 +41,10 @@ export const showInstructionsToast = () => { />

- 마우스 휠:{' '} - 확대/축소 + 확대/축소:{' '} + + 오른쪽 하단 +/- 버튼 또는 두 손가락 핀치 +

@@ -57,7 +61,19 @@ export const showInstructionsToast = () => { - {/* + 캔버스 이동:{' '} + 한 손가락 드래그 +

+
+
+ ); + + const desktopInstructions = ( +
+
How to Play
+
+ { > - - */} + + +

+ 마우스 좌클릭:{' '} + 픽셀 선택 +

+
+
+ + + +

+ 마우스 휠:{' '} + 확대/축소 +

+
+
+ + + + +

마우스 좌클릭 후 드래그: @@ -96,17 +150,18 @@ export const showInstructionsToast = () => { 좌상단 이미지 버튼 클릭

-
, - { - position: 'top-center', - autoClose: 5000, - hideProgressBar: true, - closeOnClick: true, - pauseOnHover: true, - draggable: true, - progress: undefined, - theme: 'dark', - style: { backgroundColor: '#1f2937', color: 'white' }, - } +
); + + toast(isMobile ? mobileInstructions : desktopInstructions, { + position: 'top-center', + autoClose: 5000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: 'dark', + style: { backgroundColor: '#1f2937', color: 'white' }, + }); }; diff --git a/src/hooks/useSocket.ts b/src/hooks/useSocket.ts index 68f3a61..70e2ec7 100644 --- a/src/hooks/useSocket.ts +++ b/src/hooks/useSocket.ts @@ -20,7 +20,7 @@ export const useSocket = ( canvas_id: string | undefined, onCooldownReceived?: (cooldown: CooldownData) => void ) => { - const { accessToken } = useAuthStore(); + const { accessToken, user } = useAuthStore(); const pixelCallbackRef = useRef(onPixelReceived); const cooldownCallbackRef = useRef(onCooldownReceived); const [isConnected, setIsConnected] = useState(false); @@ -78,7 +78,7 @@ export const useSocket = ( socketService.disconnect(); setIsConnected(false); }; - }, [canvas_id, accessToken]); + }, [canvas_id, accessToken, user]); const sendPixel = (pixel: PixelData) => { if (!canvas_id) return;