react-native-earl-gamepad 0.4.1 → 0.5.1
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/README.md +44 -0
- package/dist/GamepadBridge.d.ts +2 -2
- package/dist/GamepadBridge.js +22 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,6 +45,50 @@ export function Controls() {
|
|
|
45
45
|
}
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
### Control Logic Example
|
|
49
|
+
|
|
50
|
+
Here is an example of mapping D-pad events to movement vectors
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { useState, useCallback } from "react";
|
|
54
|
+
import { GamepadBridge, type DpadEvent } from "react-native-earl-gamepad";
|
|
55
|
+
type MoveKey = keyof typeof MOVES;
|
|
56
|
+
|
|
57
|
+
const MOVES: Record<string, [number, number]> = {
|
|
58
|
+
up: [1, 0],
|
|
59
|
+
down: [-1, 0],
|
|
60
|
+
right: [0, 1],
|
|
61
|
+
left: [0, -1],
|
|
62
|
+
stop: [0, 0],
|
|
63
|
+
|
|
64
|
+
axis_left_x_neg: [0, -1],
|
|
65
|
+
axis_left_x_pos: [0, 1],
|
|
66
|
+
axis_left_y_pos: [1, 0],
|
|
67
|
+
axis_left_y_neg: [-1, 0],
|
|
68
|
+
|
|
69
|
+
// add more
|
|
70
|
+
}; // example only for the control logic
|
|
71
|
+
|
|
72
|
+
export function Controls() {
|
|
73
|
+
const [active, setActive] = useState<string | null>(null);
|
|
74
|
+
|
|
75
|
+
const handleDpad = useCallback(
|
|
76
|
+
(event: DpadEvent) => {
|
|
77
|
+
const key = event.key as MoveKey;
|
|
78
|
+
if (event.pressed) {
|
|
79
|
+
if (active !== key) {
|
|
80
|
+
console.log("Dpad press", key);
|
|
81
|
+
}
|
|
82
|
+
} else if (active === key) {
|
|
83
|
+
// do something
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
[active]
|
|
87
|
+
);
|
|
88
|
+
return <GamepadBridge enabled onDpad={handleDpad} axisThreshold={0.15} />;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
48
92
|
### Hook for stateful consumption
|
|
49
93
|
|
|
50
94
|
`useGamepad` keeps pressed state and axes for you. You still need to render the provided `bridge` element once.
|
package/dist/GamepadBridge.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { StyleProp, ViewStyle } from
|
|
2
|
-
import type { AxisEvent, ButtonEvent, DpadEvent, StatusEvent } from
|
|
1
|
+
import { StyleProp, ViewStyle } from "react-native";
|
|
2
|
+
import type { AxisEvent, ButtonEvent, DpadEvent, StatusEvent } from "./types";
|
|
3
3
|
type Props = {
|
|
4
4
|
enabled?: boolean;
|
|
5
5
|
axisThreshold?: number;
|
package/dist/GamepadBridge.js
CHANGED
|
@@ -100,22 +100,34 @@ const buildBridgeHtml = (axisThreshold) => `
|
|
|
100
100
|
</script>
|
|
101
101
|
</body></html>`;
|
|
102
102
|
function GamepadBridge({ enabled = true, axisThreshold = 0.15, onDpad, onButton, onAxis, onStatus, style, }) {
|
|
103
|
+
const webviewRef = (0, react_1.useRef)(null);
|
|
103
104
|
const html = (0, react_1.useMemo)(() => buildBridgeHtml(axisThreshold), [axisThreshold]);
|
|
105
|
+
const focusBridge = (0, react_1.useCallback)(() => {
|
|
106
|
+
var _a, _b;
|
|
107
|
+
// On Android controllers, ensure the WebView is focusable so DPAD events feed navigator.getGamepads
|
|
108
|
+
const node = webviewRef.current;
|
|
109
|
+
(_a = node === null || node === void 0 ? void 0 : node.setNativeProps) === null || _a === void 0 ? void 0 : _a.call(node, { focusable: true, focusableInTouchMode: true });
|
|
110
|
+
(_b = node === null || node === void 0 ? void 0 : node.requestFocus) === null || _b === void 0 ? void 0 : _b.call(node);
|
|
111
|
+
}, []);
|
|
112
|
+
(0, react_1.useEffect)(() => {
|
|
113
|
+
if (enabled)
|
|
114
|
+
focusBridge();
|
|
115
|
+
}, [enabled, focusBridge]);
|
|
104
116
|
if (!enabled)
|
|
105
117
|
return null;
|
|
106
118
|
const handleMessage = (event) => {
|
|
107
119
|
try {
|
|
108
120
|
const data = JSON.parse(event.nativeEvent.data);
|
|
109
|
-
if (data.type ===
|
|
121
|
+
if (data.type === "dpad") {
|
|
110
122
|
onDpad === null || onDpad === void 0 ? void 0 : onDpad(data);
|
|
111
123
|
}
|
|
112
|
-
else if (data.type ===
|
|
124
|
+
else if (data.type === "button") {
|
|
113
125
|
onButton === null || onButton === void 0 ? void 0 : onButton(data);
|
|
114
126
|
}
|
|
115
|
-
else if (data.type ===
|
|
127
|
+
else if (data.type === "axis") {
|
|
116
128
|
onAxis === null || onAxis === void 0 ? void 0 : onAxis(data);
|
|
117
129
|
}
|
|
118
|
-
else if (data.type ===
|
|
130
|
+
else if (data.type === "status") {
|
|
119
131
|
onStatus === null || onStatus === void 0 ? void 0 : onStatus(data);
|
|
120
132
|
}
|
|
121
133
|
}
|
|
@@ -123,5 +135,10 @@ function GamepadBridge({ enabled = true, axisThreshold = 0.15, onDpad, onButton,
|
|
|
123
135
|
// ignore malformed messages
|
|
124
136
|
}
|
|
125
137
|
};
|
|
126
|
-
return ((0, jsx_runtime_1.jsx)(react_native_webview_1.default, { source: { html }, originWhitelist: [
|
|
138
|
+
return ((0, jsx_runtime_1.jsx)(react_native_webview_1.default, { ref: webviewRef, source: { html }, originWhitelist: ["*"], onMessage: handleMessage, javaScriptEnabled: true, onLoad: focusBridge, onLayout: focusBridge, onTouchStart: focusBridge, style: style !== null && style !== void 0 ? style : {
|
|
139
|
+
width: 1,
|
|
140
|
+
height: 1,
|
|
141
|
+
position: "absolute",
|
|
142
|
+
opacity: 0,
|
|
143
|
+
} }));
|
|
127
144
|
}
|