Skip to content

Commit 39ed813

Browse files
committed
Use PointerEvents instead of MouseEvents so game works on mobile
1 parent 9dd07c5 commit 39ed813

File tree

3 files changed

+77
-33
lines changed

3 files changed

+77
-33
lines changed

frontend/src/App.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
.drawing-canvas {
1010
border: 1px solid #ccc;
1111
border-radius: 8px;
12+
13+
/* Disable browser default touch behaviour as, for example, we do not want scroll behaviour whilst drawing on the canvas when on mobile */
14+
touch-action: none;
1215
}
1316

1417
.clear-button {

frontend/src/App.tsx

Lines changed: 68 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from "react"
22
import { socket } from "./socket"
33
import { distanceBetweenTwoPoints } from "./utils"
44
import "./App.css"
5-
5+
import { type Pointer } from "./types"
66
const CANVAS_HEIGHT = 300
77
const CANVAS_WIDTH = 300
88

@@ -37,7 +37,6 @@ const SOCKET_EVENTS_OUTBOUND = {
3737
enum GameMode {
3838
OneLine = "One Line",
3939
LineLengthLimit = "Line Length Limit",
40-
TimeLimit = "Time Limit",
4140
}
4241

4342
const App: React.FC = () => {
@@ -53,10 +52,10 @@ const App: React.FC = () => {
5352
const [lineLengthLimit, setLineLengthLimit] = useState<number | undefined>(
5453
undefined
5554
)
56-
const [lastX, setLastX] = useState<number | undefined | null>(undefined)
57-
const [lastY, setLastY] = useState<number | undefined | null>(undefined)
5855
const [currentLineLength, setCurrentLineLength] = useState<number>(0)
5956

57+
const [ongoingPointer, setOngoingPointer] = useState<Pointer | undefined | null>(undefined)
58+
6059
const shouldShowCanvas = gameMode !== undefined && gameMode !== null
6160
const isMyTurn = socket.id === turnPlayer
6261

@@ -168,12 +167,18 @@ const App: React.FC = () => {
168167
}
169168
}, [])
170169

171-
const startDrawing = (event: React.MouseEvent<HTMLCanvasElement>) => {
170+
const startDrawing = (event: React.PointerEvent<HTMLCanvasElement>) => {
172171
if (!isMyTurn) return
173172

174-
const { offsetX, offsetY } = event.nativeEvent
175-
setLastX(offsetX)
176-
setLastY(offsetY)
173+
const { pageX, pageY, pointerId } = event
174+
const canvas = canvasRef.current
175+
if (!canvas) {
176+
console.error(NO_CANVAS_ERROR)
177+
return
178+
}
179+
const rect = canvas.getBoundingClientRect()
180+
const relativeX = pageX - rect.left
181+
const relativeY = pageY - rect.top
177182

178183
const context = contextRef.current
179184
if (!context) {
@@ -184,58 +189,73 @@ const App: React.FC = () => {
184189
setIsDrawing(true)
185190

186191
context.beginPath()
187-
context.lineTo(offsetX, offsetY)
192+
context.moveTo(relativeX, relativeY)
193+
context.lineTo(relativeX, relativeY)
188194
context.stroke()
195+
setOngoingPointer({
196+
[pointerId]: {
197+
relativeX,
198+
relativeY,
199+
},
200+
})
189201

190202
socket?.emit(
191203
SOCKET_EVENTS_OUTBOUND.DRAW,
192204
context.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT).data
193205
)
194206
}
195207

196-
const draw = (event: React.MouseEvent<HTMLCanvasElement>) => {
208+
const draw = (event: React.PointerEvent<HTMLCanvasElement>) => {
197209
if (!isDrawing) return
198210

211+
const pointer = ongoingPointer?.[event.pointerId]
212+
if (!pointer) {
213+
console.error(
214+
`Could not find pointer for ${event.type} pointer id ${event.pointerId}`
215+
)
216+
return
217+
}
218+
199219
const context = contextRef.current
200220
if (!context) {
201221
console.error(NO_CONTEXT_ERROR)
202222
return
203223
}
204224

205-
const { offsetX, offsetY } = event.nativeEvent
206-
context.lineTo(offsetX, offsetY)
225+
const { pageX, pageY } = event
226+
const canvas = canvasRef.current
227+
if (!canvas) {
228+
console.error(NO_CANVAS_ERROR)
229+
return
230+
}
231+
const rect = canvas.getBoundingClientRect()
232+
const relativeX = pageX - rect.left
233+
const relativeY = pageY - rect.top
234+
235+
context.beginPath()
236+
context.moveTo(pointer.relativeX, pointer.relativeY)
237+
context.lineTo(relativeX, relativeY)
207238
context.stroke()
208239

209240
if (gameMode === GameMode.LineLengthLimit) {
210-
if (
211-
lastX === undefined ||
212-
lastX === null ||
213-
lastY === undefined ||
214-
lastY === null
215-
) {
216-
throw Error("lastX or lastY is undefined")
217-
}
218-
219241
if (
220242
lineLengthLimit !== undefined &&
221243
currentLineLength > lineLengthLimit
222244
) {
223-
stopDrawing()
245+
stopDrawing(event)
224246
setCurrentLineLength(0)
225247

226248
return
227249
}
228250

229251
const additionalLength = distanceBetweenTwoPoints({
230-
x1: lastX,
231-
y1: lastY,
232-
x2: offsetX,
233-
y2: offsetY,
252+
x1: pointer.relativeX,
253+
y1: pointer.relativeY,
254+
x2: relativeX,
255+
y2: relativeY,
234256
})
235257

236258
setCurrentLineLength((prev) => prev + additionalLength)
237-
setLastX(offsetX)
238-
setLastY(offsetY)
239259

240260
socket?.emit(
241261
SOCKET_EVENTS_OUTBOUND.DRAW,
@@ -249,11 +269,26 @@ const App: React.FC = () => {
249269
} else {
250270
throw Error(`Invalid Game Mode '${gameMode}'`)
251271
}
272+
273+
setOngoingPointer({
274+
[event.pointerId]: {
275+
relativeX: relativeX,
276+
relativeY: relativeY,
277+
},
278+
})
252279
}
253280

254-
const stopDrawing = () => {
281+
const stopDrawing = (event: React.PointerEvent<HTMLCanvasElement>) => {
255282
if (!isDrawing) return
256283

284+
const pointer = ongoingPointer?.[event.pointerId]
285+
if (!pointer) {
286+
console.error(
287+
`Could not find pointer for ${event.type} pointer id ${event.pointerId}`
288+
)
289+
return
290+
}
291+
257292
setIsDrawing(false)
258293

259294
socket.emit(SOCKET_EVENTS_OUTBOUND.END_TURN)
@@ -285,10 +320,10 @@ const App: React.FC = () => {
285320
</div>
286321
<canvas
287322
ref={canvasRef}
288-
onMouseDown={startDrawing}
289-
onMouseMove={draw}
290-
onMouseUp={stopDrawing}
291-
onMouseLeave={stopDrawing}
323+
onPointerDown={startDrawing}
324+
onPointerMove={draw}
325+
onPointerUp={stopDrawing}
326+
onPointerLeave={stopDrawing}
292327
className="drawing-canvas"
293328
/>
294329
<button type="button" onClick={clearCanvas} className="clear-button">

frontend/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type Pointer = {
2+
[key: number]: {
3+
relativeX: number
4+
relativeY: number
5+
}
6+
}

0 commit comments

Comments
 (0)