@@ -7,9 +7,16 @@ import {
7
7
ExportImageOptions ,
8
8
ExportImageType ,
9
9
Point ,
10
+ TouchExtends ,
10
11
} from "../types" ;
11
12
import { CanvasProps , CanvasRef } from "./types" ;
12
13
14
+ const TOUCH_TYPE_MAP : Record < string , string > = {
15
+ 'direct' : 'touch' ,
16
+ 'stylus' : 'pen' ,
17
+ default : 'mouse'
18
+ }
19
+
13
20
const loadImage = ( url : string ) : Promise < HTMLImageElement > =>
14
21
new Promise ( ( resolve , reject ) => {
15
22
const img = new Image ( ) ;
@@ -79,7 +86,7 @@ export const Canvas = React.forwardRef<CanvasRef, CanvasProps>((props, ref) => {
79
86
80
87
// Converts mouse coordinates to relative coordinate based on the absolute position of svg
81
88
const getCoordinates = useCallback (
82
- ( pointerEvent : React . PointerEvent < HTMLDivElement > ) : Point => {
89
+ ( pointerEvent : TouchEvent | MouseEvent ) : Point => {
83
90
const boundingArea = canvasRef . current ?. getBoundingClientRect ( ) ;
84
91
canvasSizeRef . current = boundingArea
85
92
? {
@@ -94,10 +101,20 @@ export const Canvas = React.forwardRef<CanvasRef, CanvasProps>((props, ref) => {
94
101
if ( ! boundingArea ) {
95
102
return { x : 0 , y : 0 } ;
96
103
}
104
+ let x = 0 ;
105
+ let y = 0 ;
106
+
107
+ if ( pointerEvent instanceof TouchEvent ) {
108
+ x = pointerEvent . touches [ 0 ] . pageX ;
109
+ y = pointerEvent . touches [ 0 ] . pageY ;
110
+ } else {
111
+ x = pointerEvent . pageX ;
112
+ y = pointerEvent . pageY ;
113
+ }
97
114
98
115
return {
99
- x : pointerEvent . pageX - boundingArea . left - scrollLeft ,
100
- y : pointerEvent . pageY - boundingArea . top - scrollTop ,
116
+ x : x - boundingArea . left - scrollLeft ,
117
+ y : y - boundingArea . top - scrollTop ,
101
118
} ;
102
119
} ,
103
120
[ ] ,
@@ -106,62 +123,75 @@ export const Canvas = React.forwardRef<CanvasRef, CanvasProps>((props, ref) => {
106
123
/* Mouse Handlers - Mouse down, move and up */
107
124
108
125
const handlePointerDown = useCallback (
109
- ( event : React . PointerEvent < HTMLDivElement > ) : void => {
126
+ ( event : TouchEvent | MouseEvent ) : void => {
127
+ if ( event instanceof TouchEvent && event . touches . length > 1 ) {
128
+ return ;
129
+ }
130
+ if ( readOnly ) return ;
110
131
// Allow only chosen pointer type
111
132
112
133
if (
113
- allowOnlyPointerType !== "all" &&
114
- event . pointerType !== allowOnlyPointerType
134
+ allowOnlyPointerType !== "all"
115
135
) {
116
- return ;
136
+ if ( event instanceof TouchEvent ) {
137
+ const touch = event . touches [ 0 ] as TouchExtends ;
138
+ if ( TOUCH_TYPE_MAP [ touch . touchType ] !== allowOnlyPointerType ) {
139
+ return ;
140
+ }
141
+ }
117
142
}
118
143
119
- if ( event . pointerType === "mouse" && event . button !== 0 ) return ;
144
+ event . preventDefault ( ) ;
120
145
121
- const isEraser =
122
- // eslint-disable-next-line no-bitwise
123
- event . pointerType === "pen" && ( event . buttons & 32 ) === 32 ;
124
146
const point = getCoordinates ( event ) ;
125
147
126
- onPointerDown ( point , isEraser ) ;
148
+ onPointerDown ( point ) ;
127
149
} ,
128
- [ allowOnlyPointerType , getCoordinates , onPointerDown ] ,
150
+ [ allowOnlyPointerType , getCoordinates , onPointerDown , readOnly ] ,
129
151
) ;
130
152
131
153
const handlePointerMove = useCallback (
132
- ( event : React . PointerEvent < HTMLDivElement > ) : void => {
133
- if ( ! isDrawing ) return ;
154
+ ( event : MouseEvent | TouchEvent ) : void => {
155
+ event . preventDefault ( ) ;
156
+ if ( ! isDrawing || readOnly ) return ;
134
157
135
158
// Allow only chosen pointer type
136
159
if (
137
- allowOnlyPointerType !== "all" &&
138
- event . pointerType !== allowOnlyPointerType
160
+ allowOnlyPointerType !== "all"
139
161
) {
140
- return ;
162
+ if ( event instanceof TouchEvent ) {
163
+ const touch = event . touches [ 0 ] as TouchExtends ;
164
+ if ( TOUCH_TYPE_MAP [ touch . touchType ] !== allowOnlyPointerType ) {
165
+ return ;
166
+ }
167
+ }
141
168
}
142
169
143
170
const point = getCoordinates ( event ) ;
144
171
145
172
onPointerMove ( point ) ;
146
173
} ,
147
- [ allowOnlyPointerType , getCoordinates , isDrawing , onPointerMove ] ,
174
+ [ allowOnlyPointerType , getCoordinates , isDrawing , onPointerMove , readOnly ] ,
148
175
) ;
149
176
150
177
const handlePointerUp = useCallback (
151
- ( event : React . PointerEvent < HTMLDivElement > | PointerEvent ) : void => {
152
- if ( event . pointerType === "mouse" && event . button !== 0 ) return ;
153
-
178
+ ( event : TouchEvent | MouseEvent ) : void => {
179
+ if ( readOnly ) return ;
154
180
// Allow only chosen pointer type
155
181
if (
156
- allowOnlyPointerType !== "all" &&
157
- event . pointerType !== allowOnlyPointerType
182
+ allowOnlyPointerType !== "all"
158
183
) {
159
- return ;
184
+ if ( event instanceof TouchEvent ) {
185
+ const touch = event . touches [ 0 ] as TouchExtends ;
186
+ if ( TOUCH_TYPE_MAP [ touch . touchType ] !== allowOnlyPointerType ) {
187
+ return ;
188
+ }
189
+ }
160
190
}
161
191
162
192
onPointerUp ( ) ;
163
193
} ,
164
- [ allowOnlyPointerType , onPointerUp ] ,
194
+ [ allowOnlyPointerType , onPointerUp , readOnly ] ,
165
195
) ;
166
196
167
197
/* Mouse Handlers ends */
@@ -263,13 +293,33 @@ export const Canvas = React.forwardRef<CanvasRef, CanvasProps>((props, ref) => {
263
293
} ) ) ;
264
294
265
295
/* Add event listener to Mouse up and Touch up to
266
- release drawing even when point goes out of canvas */
296
+ release drawing even when point goes out of canvas */
267
297
React . useEffect ( ( ) => {
268
- document . addEventListener ( "pointerup" , handlePointerUp ) ;
269
- return ( ) => {
270
- document . removeEventListener ( "pointerup" , handlePointerUp ) ;
271
- } ;
272
- } , [ handlePointerUp ] ) ;
298
+ const el = canvasRef . current ;
299
+ if ( el ) {
300
+ el . addEventListener ( "touchstart" , handlePointerDown ) ;
301
+ el . addEventListener ( "mousedown" , handlePointerDown ) ;
302
+
303
+ el . addEventListener ( "touchmove" , handlePointerMove ) ;
304
+ el . addEventListener ( "mousemove" , handlePointerMove ) ;
305
+
306
+ el . addEventListener ( "touchend" , handlePointerUp ) ;
307
+ el . addEventListener ( "mouseup" , handlePointerUp ) ;
308
+ document . addEventListener ( "mouseup" , handlePointerUp ) ;
309
+ return ( ) => {
310
+ el . removeEventListener ( "touchstart" , handlePointerDown ) ;
311
+ el . removeEventListener ( "mousedown" , handlePointerDown ) ;
312
+
313
+ el . removeEventListener ( "touchmove" , handlePointerMove ) ;
314
+ el . removeEventListener ( "mousemove" , handlePointerMove ) ;
315
+
316
+ el . removeEventListener ( "touchend" , handlePointerUp ) ;
317
+ el . removeEventListener ( "mouseup" , handlePointerUp ) ;
318
+ document . removeEventListener ( "mouseup" , handlePointerUp ) ;
319
+ } ;
320
+ }
321
+ return ( ) => { }
322
+ } , [ handlePointerDown , handlePointerMove , handlePointerUp ] ) ;
273
323
274
324
const eraserPaths = React . useMemo (
275
325
( ) => paths . filter ( ( path ) => ! path . drawMode ) ,
@@ -305,13 +355,12 @@ release drawing even when point goes out of canvas */
305
355
className = { className }
306
356
style = { {
307
357
touchAction : "none" ,
358
+ userSelect : "none" ,
359
+ WebkitTouchCallout : "none" ,
308
360
width,
309
361
height,
310
362
...style ,
311
363
} }
312
- onPointerDown = { readOnly ? undefined : handlePointerDown }
313
- onPointerMove = { readOnly ? undefined : handlePointerMove }
314
- onPointerUp = { readOnly ? undefined : handlePointerUp }
315
364
>
316
365
< svg
317
366
version = "1.1"
0 commit comments