Showing
3 changed files
with
102 additions
and
2 deletions
web/src/components/room/Canvas.tsx
0 → 100644
1 | +import React, { useCallback, useEffect, useRef, useState } from 'react'; | ||
2 | +import { Vector } from './types'; | ||
3 | + | ||
4 | +// 참고 : https://basketdeveloper.tistory.com/79 | ||
5 | + | ||
6 | +export const Canvas: React.FC = () => { | ||
7 | + const canvasRef = useRef<HTMLCanvasElement>(null); | ||
8 | + | ||
9 | + const [mousePosition, setMousePosition] = useState<Vector>({ x:0, y:0 }); | ||
10 | + const [isPainting, setIsPainting] = useState(false); | ||
11 | + | ||
12 | + const getCoordinates = useCallback((event: MouseEvent): Vector | undefined => { | ||
13 | + if (!canvasRef.current) { | ||
14 | + return; | ||
15 | + } else { | ||
16 | + return { | ||
17 | + x: event.pageX - canvasRef.current.offsetLeft, | ||
18 | + y: event.pageY - canvasRef.current.offsetTop | ||
19 | + }; | ||
20 | + } | ||
21 | + }, []); | ||
22 | + | ||
23 | + const drawLine = useCallback((prev: Vector, current: Vector) => { | ||
24 | + if (canvasRef.current) { | ||
25 | + const context = canvasRef.current!.getContext('2d'); | ||
26 | + if (context) { | ||
27 | + context.strokeStyle = 'black'; | ||
28 | + context.lineJoin = 'round'; | ||
29 | + context.lineWidth = 5; | ||
30 | + | ||
31 | + context.beginPath(); | ||
32 | + context.moveTo(prev.x, prev.y); | ||
33 | + context.lineTo(current.x, current.y); | ||
34 | + context.closePath(); | ||
35 | + | ||
36 | + context.stroke(); | ||
37 | + } | ||
38 | + } | ||
39 | + }, []); | ||
40 | + | ||
41 | + const startPaint = useCallback((event: MouseEvent) => { | ||
42 | + const coordinates = getCoordinates(event); | ||
43 | + if (coordinates) { | ||
44 | + setIsPainting(true); | ||
45 | + setMousePosition(coordinates); | ||
46 | + } | ||
47 | + }, []); | ||
48 | + | ||
49 | + const paint = useCallback( | ||
50 | + (event: MouseEvent) => { | ||
51 | + // 드래그 방지 | ||
52 | + event.preventDefault(); | ||
53 | + event.stopPropagation(); | ||
54 | + | ||
55 | + if (isPainting) { | ||
56 | + const newMousePosition = getCoordinates(event); | ||
57 | + if (mousePosition && newMousePosition) { | ||
58 | + drawLine(mousePosition, newMousePosition); | ||
59 | + setMousePosition(newMousePosition); | ||
60 | + } | ||
61 | + } | ||
62 | + }, | ||
63 | + [isPainting, mousePosition] | ||
64 | + ); | ||
65 | + | ||
66 | + const exitPaint = useCallback(() => { | ||
67 | + setIsPainting(false); | ||
68 | + }, []); | ||
69 | + | ||
70 | + useEffect(() => { | ||
71 | + if (canvasRef.current) { | ||
72 | + const canvas: HTMLCanvasElement = canvasRef.current; | ||
73 | + | ||
74 | + canvas.addEventListener('mousedown', startPaint); | ||
75 | + canvas.addEventListener('mousemove', paint); | ||
76 | + canvas.addEventListener('mouseup', exitPaint); | ||
77 | + canvas.addEventListener('mouseleave', exitPaint); | ||
78 | + | ||
79 | + return () => { | ||
80 | + canvas.removeEventListener('mousedown', startPaint); | ||
81 | + canvas.removeEventListener('mousemove', paint); | ||
82 | + canvas.removeEventListener('mouseup', exitPaint); | ||
83 | + canvas.removeEventListener('mouseleave', exitPaint); | ||
84 | + }; | ||
85 | + } | ||
86 | + }, [startPaint, paint, exitPaint]); | ||
87 | + | ||
88 | + return ( | ||
89 | + <div className='mx-3 px-2 py-1 rounded shadow'> | ||
90 | + <canvas ref={canvasRef} width='512' height='384' /> | ||
91 | + </div> | ||
92 | + ); | ||
93 | +} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
... | @@ -23,4 +23,9 @@ export interface UpdateRoomUser { | ... | @@ -23,4 +23,9 @@ export interface UpdateRoomUser { |
23 | export interface ChatData { | 23 | export interface ChatData { |
24 | sender: string; | 24 | sender: string; |
25 | message: string; | 25 | message: string; |
26 | -} | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
26 | +} | ||
27 | + | ||
28 | +export interface Vector { | ||
29 | + x: number; | ||
30 | + y: number; | ||
31 | +} | ... | ... |
1 | import React, { useContext } from 'react'; | 1 | import React, { useContext } from 'react'; |
2 | import { useLocation } from 'react-router'; | 2 | import { useLocation } from 'react-router'; |
3 | import { Main } from '../components/common/Main'; | 3 | import { Main } from '../components/common/Main'; |
4 | +import { Canvas } from '../components/room/Canvas'; | ||
4 | import { Chat } from '../components/room/Chat'; | 5 | import { Chat } from '../components/room/Chat'; |
5 | import { RoomInfo } from '../components/room/RoomInfo'; | 6 | import { RoomInfo } from '../components/room/RoomInfo'; |
6 | import SocketContext from '../contexts/SocketContext'; | 7 | import SocketContext from '../contexts/SocketContext'; |
... | @@ -9,7 +10,8 @@ export const Room: React.FC = () => { | ... | @@ -9,7 +10,8 @@ export const Room: React.FC = () => { |
9 | return ( | 10 | return ( |
10 | <Main> | 11 | <Main> |
11 | <RoomInfo /> | 12 | <RoomInfo /> |
12 | - <div className='w-full'> | 13 | + <div className='w-full flex'> |
14 | + <Canvas /> | ||
13 | <Chat /> | 15 | <Chat /> |
14 | </div> | 16 | </div> |
15 | </Main> | 17 | </Main> | ... | ... |
-
Please register or login to post a comment