@rpg-engine/long-bow 0.7.98 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/DPad/JoystickDPad.d.ts +4 -2
- package/dist/long-bow.cjs.development.js +210 -178
- package/dist/long-bow.cjs.development.js.map +1 -1
- package/dist/long-bow.cjs.production.min.js +1 -1
- package/dist/long-bow.cjs.production.min.js.map +1 -1
- package/dist/long-bow.esm.js +211 -179
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DPad/JoystickDPad.tsx +276 -171
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
1
|
+
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
4
|
interface IDPadContainerProps {
|
|
@@ -8,6 +8,12 @@ interface IDPadContainerProps {
|
|
|
8
8
|
disabled?: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
interface IDPadButtonProps {
|
|
12
|
+
size?: number;
|
|
13
|
+
isPressed?: boolean;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
interface IDPadOptions {
|
|
12
18
|
/** Opacity of the entire component (0-1) */
|
|
13
19
|
opacity?: number;
|
|
@@ -22,179 +28,16 @@ interface IDPadOptions {
|
|
|
22
28
|
interface IDPadProps {
|
|
23
29
|
/** Callback fired when a direction is pressed */
|
|
24
30
|
onDirectionPress?: (direction: 'up' | 'down' | 'left' | 'right') => void;
|
|
31
|
+
/** Callback fired when a direction is released */
|
|
32
|
+
onDirectionRelease?: (direction: 'up' | 'down' | 'left' | 'right') => void;
|
|
25
33
|
/** Whether the component is disabled */
|
|
26
34
|
disabled?: boolean;
|
|
27
35
|
/** Additional options for customizing the D-pad */
|
|
28
36
|
options?: IDPadOptions;
|
|
29
37
|
}
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
disabled = false,
|
|
34
|
-
options = {},
|
|
35
|
-
}: IDPadProps): JSX.Element => {
|
|
36
|
-
const {
|
|
37
|
-
opacity = 1,
|
|
38
|
-
showBackground = false,
|
|
39
|
-
size = 100,
|
|
40
|
-
pressInterval = 500,
|
|
41
|
-
} = options;
|
|
42
|
-
|
|
43
|
-
const [pressedButtons, setPressedButtons] = useState<Set<string>>(new Set());
|
|
44
|
-
const intervalRef = useRef<number | null>(null);
|
|
45
|
-
const activeDirectionRef = useRef<'up' | 'down' | 'left' | 'right' | null>(
|
|
46
|
-
null
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
const clearPressInterval = useCallback(() => {
|
|
50
|
-
if (intervalRef.current) {
|
|
51
|
-
window.clearInterval(intervalRef.current);
|
|
52
|
-
intervalRef.current = null;
|
|
53
|
-
}
|
|
54
|
-
activeDirectionRef.current = null;
|
|
55
|
-
}, []);
|
|
56
|
-
|
|
57
|
-
const handleDirectionPress = useCallback(
|
|
58
|
-
(direction: 'up' | 'down' | 'left' | 'right') => {
|
|
59
|
-
if (disabled) return;
|
|
60
|
-
|
|
61
|
-
// Clear any existing interval
|
|
62
|
-
clearPressInterval();
|
|
63
|
-
|
|
64
|
-
// Set the active direction
|
|
65
|
-
activeDirectionRef.current = direction;
|
|
66
|
-
setPressedButtons(prev => new Set(prev).add(direction));
|
|
67
|
-
|
|
68
|
-
// Trigger first press immediately
|
|
69
|
-
onDirectionPress?.(direction);
|
|
70
|
-
|
|
71
|
-
// Set up the interval for continuous press
|
|
72
|
-
intervalRef.current = window.setInterval(() => {
|
|
73
|
-
if (activeDirectionRef.current === direction) {
|
|
74
|
-
onDirectionPress?.(direction);
|
|
75
|
-
}
|
|
76
|
-
}, pressInterval);
|
|
77
|
-
},
|
|
78
|
-
[disabled, onDirectionPress, pressInterval, clearPressInterval]
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
const handleDirectionRelease = useCallback(
|
|
82
|
-
(direction: 'up' | 'down' | 'left' | 'right') => {
|
|
83
|
-
setPressedButtons(prev => {
|
|
84
|
-
const next = new Set(prev);
|
|
85
|
-
next.delete(direction);
|
|
86
|
-
return next;
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
if (activeDirectionRef.current === direction) {
|
|
90
|
-
clearPressInterval();
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
[clearPressInterval]
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
// Cleanup on unmount
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
return () => {
|
|
99
|
-
clearPressInterval();
|
|
100
|
-
};
|
|
101
|
-
}, [clearPressInterval]);
|
|
102
|
-
|
|
103
|
-
const preventDefault = (e: React.MouseEvent | React.TouchEvent) => {
|
|
104
|
-
e.preventDefault();
|
|
105
|
-
e.stopPropagation();
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<DPadContainer
|
|
110
|
-
opacity={opacity}
|
|
111
|
-
showBackground={showBackground}
|
|
112
|
-
size={size}
|
|
113
|
-
disabled={disabled}
|
|
114
|
-
onContextMenu={preventDefault}
|
|
115
|
-
>
|
|
116
|
-
<DPadButton
|
|
117
|
-
className="up"
|
|
118
|
-
onMouseDown={() => handleDirectionPress('up')}
|
|
119
|
-
onMouseUp={() => handleDirectionRelease('up')}
|
|
120
|
-
onMouseLeave={() => handleDirectionRelease('up')}
|
|
121
|
-
onTouchStart={() => handleDirectionPress('up')}
|
|
122
|
-
onTouchEnd={() => handleDirectionRelease('up')}
|
|
123
|
-
onContextMenu={preventDefault}
|
|
124
|
-
size={size}
|
|
125
|
-
isPressed={pressedButtons.has('up')}
|
|
126
|
-
disabled={disabled}
|
|
127
|
-
/>
|
|
128
|
-
<DPadButton
|
|
129
|
-
className="right"
|
|
130
|
-
onMouseDown={() => handleDirectionPress('right')}
|
|
131
|
-
onMouseUp={() => handleDirectionRelease('right')}
|
|
132
|
-
onMouseLeave={() => handleDirectionRelease('right')}
|
|
133
|
-
onTouchStart={() => handleDirectionPress('right')}
|
|
134
|
-
onTouchEnd={() => handleDirectionRelease('right')}
|
|
135
|
-
onContextMenu={preventDefault}
|
|
136
|
-
size={size}
|
|
137
|
-
isPressed={pressedButtons.has('right')}
|
|
138
|
-
disabled={disabled}
|
|
139
|
-
/>
|
|
140
|
-
<DPadButton
|
|
141
|
-
className="down"
|
|
142
|
-
onMouseDown={() => handleDirectionPress('down')}
|
|
143
|
-
onMouseUp={() => handleDirectionRelease('down')}
|
|
144
|
-
onMouseLeave={() => handleDirectionRelease('down')}
|
|
145
|
-
onTouchStart={() => handleDirectionPress('down')}
|
|
146
|
-
onTouchEnd={() => handleDirectionRelease('down')}
|
|
147
|
-
onContextMenu={preventDefault}
|
|
148
|
-
size={size}
|
|
149
|
-
isPressed={pressedButtons.has('down')}
|
|
150
|
-
disabled={disabled}
|
|
151
|
-
/>
|
|
152
|
-
<DPadButton
|
|
153
|
-
className="left"
|
|
154
|
-
onMouseDown={() => handleDirectionPress('left')}
|
|
155
|
-
onMouseUp={() => handleDirectionRelease('left')}
|
|
156
|
-
onMouseLeave={() => handleDirectionRelease('left')}
|
|
157
|
-
onTouchStart={() => handleDirectionPress('left')}
|
|
158
|
-
onTouchEnd={() => handleDirectionRelease('left')}
|
|
159
|
-
onContextMenu={preventDefault}
|
|
160
|
-
size={size}
|
|
161
|
-
isPressed={pressedButtons.has('left')}
|
|
162
|
-
disabled={disabled}
|
|
163
|
-
/>
|
|
164
|
-
<DPadCenter
|
|
165
|
-
size={size}
|
|
166
|
-
disabled={disabled}
|
|
167
|
-
onContextMenu={preventDefault}
|
|
168
|
-
/>
|
|
169
|
-
</DPadContainer>
|
|
170
|
-
);
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const DPadContainer = styled.div<IDPadContainerProps>`
|
|
174
|
-
width: ${props => props.size ?? 100}px;
|
|
175
|
-
height: ${props => props.size ?? 100}px;
|
|
176
|
-
position: relative;
|
|
177
|
-
background: ${props => (props.showBackground ? '#b8b8b8' : 'transparent')};
|
|
178
|
-
border-radius: 50%;
|
|
179
|
-
box-shadow: ${props =>
|
|
180
|
-
props.showBackground
|
|
181
|
-
? 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 4px 8px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.2)'
|
|
182
|
-
: 'none'};
|
|
183
|
-
opacity: ${props => (props.disabled ? 0.5 : props.opacity ?? 1)};
|
|
184
|
-
user-select: none;
|
|
185
|
-
cursor: ${props => (props.disabled ? 'not-allowed' : 'default')};
|
|
186
|
-
transition: opacity 0.2s ease;
|
|
187
|
-
touch-action: none;
|
|
188
|
-
-webkit-tap-highlight-color: transparent;
|
|
189
|
-
`;
|
|
190
|
-
|
|
191
|
-
interface IDPadButtonProps {
|
|
192
|
-
size?: number;
|
|
193
|
-
isPressed?: boolean;
|
|
194
|
-
disabled?: boolean;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const DPadButton = styled.div<IDPadButtonProps>`
|
|
39
|
+
// Memoize the styled components since they don't depend on props that change frequently
|
|
40
|
+
const DPadButton = memo(styled.div<IDPadButtonProps>`
|
|
198
41
|
position: absolute;
|
|
199
42
|
background: ${props => (props.isPressed ? '#363636' : '#424242')};
|
|
200
43
|
box-shadow: ${props =>
|
|
@@ -285,9 +128,9 @@ const DPadButton = styled.div<IDPadButtonProps>`
|
|
|
285
128
|
transform: translate(50%, -50%);
|
|
286
129
|
}
|
|
287
130
|
}
|
|
288
|
-
|
|
131
|
+
`);
|
|
289
132
|
|
|
290
|
-
const DPadCenter = styled.div<IDPadButtonProps>`
|
|
133
|
+
const DPadCenter = memo(styled.div<IDPadButtonProps>`
|
|
291
134
|
position: absolute;
|
|
292
135
|
width: ${props => (props.size ?? 100) * 0.3}px;
|
|
293
136
|
height: ${props => (props.size ?? 100) * 0.3}px;
|
|
@@ -315,4 +158,266 @@ const DPadCenter = styled.div<IDPadButtonProps>`
|
|
|
315
158
|
pointer-events: none;
|
|
316
159
|
box-shadow: inset 0 0 2px rgba(0, 0, 0, 0.8);
|
|
317
160
|
}
|
|
318
|
-
|
|
161
|
+
`);
|
|
162
|
+
|
|
163
|
+
const DPadContainer = memo(styled.div<IDPadContainerProps>`
|
|
164
|
+
width: ${props => props.size ?? 100}px;
|
|
165
|
+
height: ${props => props.size ?? 100}px;
|
|
166
|
+
position: relative;
|
|
167
|
+
background: ${props => (props.showBackground ? '#b8b8b8' : 'transparent')};
|
|
168
|
+
border-radius: 50%;
|
|
169
|
+
box-shadow: ${props =>
|
|
170
|
+
props.showBackground
|
|
171
|
+
? 'inset 0 0 10px rgba(0, 0, 0, 0.3), 0 4px 8px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.2)'
|
|
172
|
+
: 'none'};
|
|
173
|
+
opacity: ${props => (props.disabled ? 0.5 : props.opacity ?? 1)};
|
|
174
|
+
user-select: none;
|
|
175
|
+
cursor: ${props => (props.disabled ? 'not-allowed' : 'default')};
|
|
176
|
+
transition: opacity 0.2s ease;
|
|
177
|
+
touch-action: none;
|
|
178
|
+
-webkit-tap-highlight-color: transparent;
|
|
179
|
+
`);
|
|
180
|
+
|
|
181
|
+
export const JoystickDPad = memo(
|
|
182
|
+
({
|
|
183
|
+
onDirectionPress,
|
|
184
|
+
onDirectionRelease,
|
|
185
|
+
disabled = false,
|
|
186
|
+
options = {},
|
|
187
|
+
}: IDPadProps): JSX.Element => {
|
|
188
|
+
const {
|
|
189
|
+
opacity = 1,
|
|
190
|
+
showBackground = false,
|
|
191
|
+
size = 100,
|
|
192
|
+
pressInterval = 500,
|
|
193
|
+
} = options;
|
|
194
|
+
|
|
195
|
+
// Use refs for values that don't need to trigger re-renders
|
|
196
|
+
const [pressedButtons, setPressedButtons] = useState<Set<string>>(
|
|
197
|
+
new Set()
|
|
198
|
+
);
|
|
199
|
+
const intervalRef = useRef<number | null>(null);
|
|
200
|
+
const activeDirectionRef = useRef<'up' | 'down' | 'left' | 'right' | null>(
|
|
201
|
+
null
|
|
202
|
+
);
|
|
203
|
+
const touchStartRef = useRef<{ x: number; y: number } | null>(null);
|
|
204
|
+
const isPressedRef = useRef<boolean>(false);
|
|
205
|
+
|
|
206
|
+
const clearPressInterval = useCallback(() => {
|
|
207
|
+
if (intervalRef.current !== null) {
|
|
208
|
+
window.clearInterval(intervalRef.current);
|
|
209
|
+
intervalRef.current = null;
|
|
210
|
+
}
|
|
211
|
+
activeDirectionRef.current = null;
|
|
212
|
+
}, []);
|
|
213
|
+
|
|
214
|
+
const clearAllPresses = useCallback(() => {
|
|
215
|
+
if (activeDirectionRef.current && onDirectionRelease) {
|
|
216
|
+
onDirectionRelease(activeDirectionRef.current);
|
|
217
|
+
}
|
|
218
|
+
clearPressInterval();
|
|
219
|
+
setPressedButtons(new Set());
|
|
220
|
+
activeDirectionRef.current = null;
|
|
221
|
+
isPressedRef.current = false;
|
|
222
|
+
}, [clearPressInterval, onDirectionRelease]);
|
|
223
|
+
|
|
224
|
+
const handleDirectionPress = useCallback(
|
|
225
|
+
(direction: 'up' | 'down' | 'left' | 'right') => {
|
|
226
|
+
if (disabled) return;
|
|
227
|
+
|
|
228
|
+
// Clear any existing presses first
|
|
229
|
+
clearAllPresses();
|
|
230
|
+
|
|
231
|
+
// Set new direction
|
|
232
|
+
activeDirectionRef.current = direction;
|
|
233
|
+
isPressedRef.current = true;
|
|
234
|
+
setPressedButtons(new Set([direction]));
|
|
235
|
+
onDirectionPress?.(direction);
|
|
236
|
+
|
|
237
|
+
intervalRef.current = window.setInterval(() => {
|
|
238
|
+
if (activeDirectionRef.current === direction) {
|
|
239
|
+
onDirectionPress?.(direction);
|
|
240
|
+
} else {
|
|
241
|
+
clearPressInterval();
|
|
242
|
+
}
|
|
243
|
+
}, pressInterval);
|
|
244
|
+
},
|
|
245
|
+
[
|
|
246
|
+
disabled,
|
|
247
|
+
onDirectionPress,
|
|
248
|
+
pressInterval,
|
|
249
|
+
clearPressInterval,
|
|
250
|
+
clearAllPresses,
|
|
251
|
+
]
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const handleDirectionRelease = useCallback(
|
|
255
|
+
(direction: 'up' | 'down' | 'left' | 'right') => {
|
|
256
|
+
if (activeDirectionRef.current === direction) {
|
|
257
|
+
clearAllPresses();
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
[clearAllPresses]
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const handleTouchStart = useCallback(
|
|
264
|
+
(e: React.TouchEvent, direction: 'up' | 'down' | 'left' | 'right') => {
|
|
265
|
+
const touch = e.touches[0];
|
|
266
|
+
if (touch) {
|
|
267
|
+
touchStartRef.current = { x: touch.clientX, y: touch.clientY };
|
|
268
|
+
handleDirectionPress(direction);
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
[handleDirectionPress]
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
const handleTouchMove = useCallback(
|
|
275
|
+
(e: React.TouchEvent) => {
|
|
276
|
+
const touch = e.touches[0];
|
|
277
|
+
if (!touch || !touchStartRef.current) return;
|
|
278
|
+
|
|
279
|
+
const { x: startX, y: startY } = touchStartRef.current;
|
|
280
|
+
const deltaX = touch.clientX - startX;
|
|
281
|
+
const deltaY = touch.clientY - startY;
|
|
282
|
+
|
|
283
|
+
// Calculate angle and distance
|
|
284
|
+
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
|
285
|
+
const angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
|
|
286
|
+
|
|
287
|
+
// Only trigger if we've moved enough
|
|
288
|
+
const threshold = size * 0.15; // Adaptive threshold based on d-pad size
|
|
289
|
+
if (distance < threshold) return;
|
|
290
|
+
|
|
291
|
+
let newDirection: 'up' | 'down' | 'left' | 'right' | null = null;
|
|
292
|
+
|
|
293
|
+
// Determine direction based on angle
|
|
294
|
+
if (angle > -45 && angle <= 45) newDirection = 'right';
|
|
295
|
+
else if (angle > 45 && angle <= 135) newDirection = 'down';
|
|
296
|
+
else if (angle > 135 || angle <= -135) newDirection = 'left';
|
|
297
|
+
else if (angle > -135 && angle <= -45) newDirection = 'up';
|
|
298
|
+
|
|
299
|
+
if (newDirection && newDirection !== activeDirectionRef.current) {
|
|
300
|
+
handleDirectionPress(newDirection);
|
|
301
|
+
// Update touch start to current position to prevent jitter
|
|
302
|
+
touchStartRef.current = { x: touch.clientX, y: touch.clientY };
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
[handleDirectionPress, size]
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
// Add a new cleanup function for touch events
|
|
309
|
+
const cleanupTouchEvents = useCallback(() => {
|
|
310
|
+
touchStartRef.current = null;
|
|
311
|
+
if (activeDirectionRef.current) {
|
|
312
|
+
handleDirectionRelease(activeDirectionRef.current);
|
|
313
|
+
}
|
|
314
|
+
}, [handleDirectionRelease]);
|
|
315
|
+
|
|
316
|
+
// Enhance the touch end handler
|
|
317
|
+
const handleTouchEnd = useCallback(() => {
|
|
318
|
+
cleanupTouchEvents();
|
|
319
|
+
}, [cleanupTouchEvents]);
|
|
320
|
+
|
|
321
|
+
// Add touch cancel handler
|
|
322
|
+
const handleTouchCancel = useCallback(() => {
|
|
323
|
+
cleanupTouchEvents();
|
|
324
|
+
}, [cleanupTouchEvents]);
|
|
325
|
+
|
|
326
|
+
// Enhance cleanup effect
|
|
327
|
+
useEffect(() => {
|
|
328
|
+
if (disabled) {
|
|
329
|
+
clearAllPresses();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const handleBlur = () => {
|
|
333
|
+
clearAllPresses();
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const handleVisibilityChange = () => {
|
|
337
|
+
if (document.hidden) {
|
|
338
|
+
clearAllPresses();
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const handlePointerUp = () => {
|
|
343
|
+
// Global pointer up as fallback for stuck buttons
|
|
344
|
+
if (isPressedRef.current) {
|
|
345
|
+
clearAllPresses();
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
window.addEventListener('blur', handleBlur);
|
|
350
|
+
window.addEventListener('pointerup', handlePointerUp);
|
|
351
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
352
|
+
|
|
353
|
+
return () => {
|
|
354
|
+
clearAllPresses();
|
|
355
|
+
window.removeEventListener('blur', handleBlur);
|
|
356
|
+
window.removeEventListener('pointerup', handlePointerUp);
|
|
357
|
+
document.removeEventListener(
|
|
358
|
+
'visibilitychange',
|
|
359
|
+
handleVisibilityChange
|
|
360
|
+
);
|
|
361
|
+
};
|
|
362
|
+
}, [disabled, clearAllPresses]);
|
|
363
|
+
|
|
364
|
+
// Memoize the preventDefault handler
|
|
365
|
+
const preventDefault = useCallback(
|
|
366
|
+
(e: React.MouseEvent | React.TouchEvent) => {
|
|
367
|
+
e.preventDefault();
|
|
368
|
+
e.stopPropagation();
|
|
369
|
+
},
|
|
370
|
+
[]
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
// Memoize button props to prevent unnecessary re-renders
|
|
374
|
+
const buttonProps = useCallback(
|
|
375
|
+
(direction: 'up' | 'down' | 'left' | 'right') => ({
|
|
376
|
+
onMouseDown: () => handleDirectionPress(direction),
|
|
377
|
+
onMouseUp: () => handleDirectionRelease(direction),
|
|
378
|
+
onMouseLeave: () => handleDirectionRelease(direction),
|
|
379
|
+
onTouchStart: (e: React.TouchEvent) => handleTouchStart(e, direction),
|
|
380
|
+
onTouchMove: handleTouchMove,
|
|
381
|
+
onTouchEnd: handleTouchEnd,
|
|
382
|
+
onContextMenu: preventDefault,
|
|
383
|
+
size,
|
|
384
|
+
isPressed: pressedButtons.has(direction),
|
|
385
|
+
disabled,
|
|
386
|
+
}),
|
|
387
|
+
[
|
|
388
|
+
handleDirectionPress,
|
|
389
|
+
handleDirectionRelease,
|
|
390
|
+
handleTouchStart,
|
|
391
|
+
handleTouchMove,
|
|
392
|
+
handleTouchEnd,
|
|
393
|
+
preventDefault,
|
|
394
|
+
size,
|
|
395
|
+
pressedButtons,
|
|
396
|
+
disabled,
|
|
397
|
+
]
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
return (
|
|
401
|
+
<DPadContainer
|
|
402
|
+
opacity={opacity}
|
|
403
|
+
showBackground={showBackground}
|
|
404
|
+
size={size}
|
|
405
|
+
disabled={disabled}
|
|
406
|
+
onContextMenu={preventDefault}
|
|
407
|
+
onTouchCancel={handleTouchCancel}
|
|
408
|
+
>
|
|
409
|
+
<DPadButton className="up" {...buttonProps('up')} />
|
|
410
|
+
<DPadButton className="right" {...buttonProps('right')} />
|
|
411
|
+
<DPadButton className="down" {...buttonProps('down')} />
|
|
412
|
+
<DPadButton className="left" {...buttonProps('left')} />
|
|
413
|
+
<DPadCenter
|
|
414
|
+
size={size}
|
|
415
|
+
disabled={disabled}
|
|
416
|
+
onContextMenu={preventDefault}
|
|
417
|
+
/>
|
|
418
|
+
</DPadContainer>
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
JoystickDPad.displayName = 'JoystickDPad';
|