pixijs-input-devices 0.8.1-0 → 0.9.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/README.md +204 -119
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +386 -146
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,45 +1,57 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
<h1 align="center">PixiJS Input Devices</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center"><i>Device management, navigation for pointer-based UIs, and named input bindings.</i></p>
|
|
4
|
+
|
|
5
|
+
<p align="center"><a href="https://github.com/reececomo/pixijs-input-devices/blob/main/LICENSE"><img src="https://badgen.net/npm/license/pixijs-input-devices" alt="License"></a> <a href="https://github.com/reececomo/pixijs-input-devices/actions/workflows/tests.yml"><img src="https://github.com/reececomo/pixijs-input-devices/actions/workflows/tests.yml/badge.svg" alt="Tests"></a> <a href="https://www.npmjs.com/package/pixijs-input-devices"><img src="https://img.shields.io/npm/dm/pixijs-input-devices.svg" alt="Downloads per month"></a> <a href="https://www.npmjs.com/package/pixijs-input-devices"><img src="https://img.shields.io/npm/v/pixijs-input-devices.svg" alt="NPM version"></a></p>
|
|
6
|
+
|
|
7
|
+
<p align="center"><img src="./hero.png" /></p>
|
|
8
|
+
|
|
9
|
+
<table align="center">
|
|
10
|
+
<thead>
|
|
11
|
+
<tr>
|
|
12
|
+
<th></th>
|
|
13
|
+
<th></th>
|
|
14
|
+
</tr>
|
|
15
|
+
</thead>
|
|
16
|
+
<tbody>
|
|
17
|
+
<tr>
|
|
18
|
+
<td>🎮 <a href="#keyboard">Keyboard</a>, <a href="#gamepaddevice">gamepads</a>, <a href="#custom-devices">or custom</a></td>
|
|
19
|
+
<td>✨ Configurable <a href="#named-binds">binds</a></td>
|
|
20
|
+
</tr>
|
|
21
|
+
<tr>
|
|
22
|
+
<td>🔮 Completely customizable</td>
|
|
23
|
+
<td>🧭 Navigate <a href="#uinavigation-api">pointer-based UIs</a></td>
|
|
24
|
+
</tr>
|
|
25
|
+
<tr>
|
|
26
|
+
<td>🎼 Haptic-feedback support<sup><a href="https://caniuse.com/mdn-api_gamepad_vibrationactuator">[1]</a></td>
|
|
27
|
+
<td>🌐 Fully <a href="#keyboard-layout---detection">international</a></td>
|
|
28
|
+
</tr>
|
|
29
|
+
<tr>
|
|
30
|
+
<td>🍃 Lightweight, zero dependencies</td>
|
|
31
|
+
<td>✨ Supports PixiJS v8+</td>
|
|
32
|
+
</tr>
|
|
33
|
+
</tbody>
|
|
34
|
+
</table>
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
#### Set default binds
|
|
15
38
|
|
|
16
39
|
```ts
|
|
17
|
-
import {
|
|
40
|
+
import { KeyboardDevice, GamepadDevice } from "pixijs-input-devices";
|
|
18
41
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
right: ["KeyD", "ArrowRight"]
|
|
42
|
+
KeyboardDevice.configureBinds({
|
|
43
|
+
Jump : [ "Space" ],
|
|
44
|
+
Left : [ "KeyA", "ArrowLeft" ],
|
|
45
|
+
Right : [ "KeyD", "ArrowRight" ],
|
|
24
46
|
});
|
|
25
47
|
|
|
26
|
-
// 🎮 all gamepads
|
|
27
48
|
GamepadDevice.configureDefaultBinds({
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
// 🎮 individual gamepad
|
|
34
|
-
InputDevice.gamepads[0].configurBinds({
|
|
35
|
-
left: ["LeftStickLeft"],
|
|
36
|
-
right: ["LeftStickRight"]
|
|
49
|
+
Jump: [ "Face1" ],
|
|
50
|
+
Left: [ "LeftStickLeft", "DpadLeft" ],
|
|
51
|
+
Right: [ "LeftStickRight", "DpadRight" ],
|
|
37
52
|
});
|
|
38
53
|
```
|
|
39
54
|
|
|
40
|
-
> [!TIP]
|
|
41
|
-
> See [**Named binds**](#named-binds) for more information on configuring devices.
|
|
42
|
-
|
|
43
55
|
## Basic Usage
|
|
44
56
|
|
|
45
57
|
```ts
|
|
@@ -48,14 +60,14 @@ let moveX = 0;
|
|
|
48
60
|
|
|
49
61
|
for (let device of InputDevice.devices)
|
|
50
62
|
{
|
|
51
|
-
if (device.bindDown("
|
|
52
|
-
if (device.bindDown("
|
|
53
|
-
if (device.bindDown("
|
|
63
|
+
if (device.bindDown("Jump")) jump = true;
|
|
64
|
+
if (device.bindDown("Left")) moveX = -1;
|
|
65
|
+
if (device.bindDown("Right")) moveX = 1;
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
if (device.type === "gamepad" && device.leftJoystick.x)
|
|
67
|
+
if (device.type === "gamepad")
|
|
57
68
|
{
|
|
58
|
-
|
|
69
|
+
// 🎮 dual analog
|
|
70
|
+
if (device.leftJoystick.x) moveX = device.leftJoystick.x;
|
|
59
71
|
}
|
|
60
72
|
}
|
|
61
73
|
```
|
|
@@ -64,10 +76,42 @@ for (let device of InputDevice.devices)
|
|
|
64
76
|
|
|
65
77
|
```ts
|
|
66
78
|
// targeted
|
|
67
|
-
device.onBindDown("
|
|
79
|
+
device.onBindDown("Menu", ({ device }) => { });
|
|
68
80
|
|
|
69
81
|
// global
|
|
70
|
-
InputDevice.onBindDown("
|
|
82
|
+
InputDevice.onBindDown("Menu", ({ device }) => { });
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Setup bind names
|
|
86
|
+
|
|
87
|
+
Add the following snippet to set your bind names. Interface keys are ignored, but may be
|
|
88
|
+
used for grouping.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// my-binds.ts
|
|
92
|
+
|
|
93
|
+
export {};
|
|
94
|
+
|
|
95
|
+
declare module "pixijs-input-devices"
|
|
96
|
+
{
|
|
97
|
+
interface BindValues
|
|
98
|
+
{
|
|
99
|
+
inGame:
|
|
100
|
+
| "Jump"
|
|
101
|
+
| "Left"
|
|
102
|
+
| "Right"
|
|
103
|
+
| "Crouch";
|
|
104
|
+
|
|
105
|
+
menu:
|
|
106
|
+
| "Mute"
|
|
107
|
+
| "Pause";
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface DeviceMetadata
|
|
111
|
+
{
|
|
112
|
+
playerID?: number;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
71
115
|
```
|
|
72
116
|
|
|
73
117
|
## 💿 Install
|
|
@@ -117,15 +161,13 @@ Ticker.shared.add(() => InputDevice.update())
|
|
|
117
161
|
**3.** (Optional) enable the UINavigation API
|
|
118
162
|
|
|
119
163
|
```ts
|
|
120
|
-
|
|
121
|
-
import { UINavigation, registerPixiJSNavigationMixin } from 'pixijs-input-devices'
|
|
164
|
+
const app = new PIXI.Application({ /* … */ });
|
|
122
165
|
|
|
166
|
+
// Register navigation mixin
|
|
167
|
+
registerPixiJSNavigationMixin(PIXI.Container);
|
|
123
168
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// enable the navigation API
|
|
127
|
-
UINavigation.enable(app.stage)
|
|
128
|
-
registerPixiJSNavigationMixin(PIXI.Container)
|
|
169
|
+
// Configure the navigation API on the root container
|
|
170
|
+
UINavigation.enable(app.stage);
|
|
129
171
|
```
|
|
130
172
|
|
|
131
173
|
✨ You are now ready to use inputs!
|
|
@@ -188,14 +230,15 @@ InputDevice.off("deviceadded") // stop listening
|
|
|
188
230
|
| `"lastdevicechanged"` | `{device}` | The _last interacted device_ has changed. |
|
|
189
231
|
|
|
190
232
|
|
|
191
|
-
#### InputDevice -
|
|
233
|
+
#### InputDevice - onBind*() Events
|
|
192
234
|
|
|
193
235
|
You may also subscribe globally to **named bind** events:
|
|
194
236
|
|
|
195
237
|
```ts
|
|
196
|
-
InputDevice
|
|
197
|
-
|
|
198
|
-
|
|
238
|
+
InputDevice
|
|
239
|
+
.onBindDown("MyBind", e => console.debug(e.name + " pressed"))
|
|
240
|
+
.onBindUp("MyBind", e => console.debug(e.name + " released"))
|
|
241
|
+
.onBind("MyBind", e => console.debug(e.name + e.pressed ? " pressed" : " released"));
|
|
199
242
|
```
|
|
200
243
|
|
|
201
244
|
### Keyboard
|
|
@@ -451,12 +494,11 @@ These can then be used with either the real-time and event-based APIs.
|
|
|
451
494
|
#### Event-based:
|
|
452
495
|
|
|
453
496
|
```ts
|
|
454
|
-
// listen to
|
|
497
|
+
// listen to ANY device:
|
|
455
498
|
InputDevice.onBindDown("toggleGraphics", (e) => toggleGraphics())
|
|
456
499
|
|
|
457
500
|
// listen to specific devices:
|
|
458
|
-
|
|
459
|
-
InputDevice.gamepads[0].onBindDown("jump", (e) => doJump())
|
|
501
|
+
device.onBindDown("Jump", (e) => doJump())
|
|
460
502
|
```
|
|
461
503
|
|
|
462
504
|
#### Real-time:
|
|
@@ -465,13 +507,13 @@ InputDevice.gamepads[0].onBindDown("jump", (e) => doJump())
|
|
|
465
507
|
let jump = false, crouch = false, moveX = 0
|
|
466
508
|
|
|
467
509
|
const keyboard = InputDevice.keyboard
|
|
468
|
-
if (keyboard.bindDown("
|
|
510
|
+
if (keyboard.bindDown("Jump")) jump = true
|
|
469
511
|
if (keyboard.bindDown("crouch")) crouch = true
|
|
470
512
|
if (keyboard.key.ArrowLeft) moveX = -1
|
|
471
513
|
else if (keyboard.key.ArrowRight) moveX = 1
|
|
472
514
|
|
|
473
515
|
for (const gamepad of InputDevice.gamepads) {
|
|
474
|
-
if (gamepad.bindDown("
|
|
516
|
+
if (gamepad.bindDown("Jump")) jump = true
|
|
475
517
|
if (gamepad.bindDown("crouch")) crouch = true
|
|
476
518
|
|
|
477
519
|
// gamepads have additional analog inputs
|
|
@@ -496,12 +538,19 @@ registerPixiJSNavigationMixin(PIXI.Container)
|
|
|
496
538
|
|
|
497
539
|
Navigation should now work automatically if your buttons handle these events:
|
|
498
540
|
|
|
499
|
-
- `"pointerdown"` – i.e. Trigger / show press effect
|
|
541
|
+
- `"pointerdown"`, `"pointerup"` or `"pointertap"` – i.e. Trigger / show press effect
|
|
542
|
+
- `"pointerenter"` or `"pointerover"` – i.e. Select / show hover effect
|
|
543
|
+
- `"pointerleave"` or `"pointerout"` – i.e. Deselect / reset
|
|
500
544
|
|
|
501
|
-
|
|
545
|
+
You can override these mappings manually:
|
|
502
546
|
|
|
503
|
-
|
|
504
|
-
|
|
547
|
+
```ts
|
|
548
|
+
// defaults:
|
|
549
|
+
UINavigation.options.events.focus = [ "pointerenter", "pointerover" ];
|
|
550
|
+
UINavigation.options.events.blur = [ "pointerleave", "pointerout" ];
|
|
551
|
+
UINavigation.options.events.press = [ "pointerdown" ];
|
|
552
|
+
UINavigation.options.events.release = [ "pointerup", "pointertap" ];
|
|
553
|
+
```
|
|
505
554
|
|
|
506
555
|
> [!TIP]
|
|
507
556
|
> 🖱️ **Seamless navigation:** Manually set `UINavigation.focusTarget = <target>`
|
|
@@ -512,6 +561,19 @@ But in order to really make use, you should also set:
|
|
|
512
561
|
> **Auto-focus:** Set a container's `navigationPriority` to a value above `0`
|
|
513
562
|
> to become the default selection in a context.
|
|
514
563
|
|
|
564
|
+
### Spatial navigation
|
|
565
|
+
|
|
566
|
+
By default the "NavigateUp", "NavigateLeft", "NavigateRight" and "NavigateDown" events
|
|
567
|
+
use global screen space to move to the nearest UI in that direction, using a heuristic.
|
|
568
|
+
|
|
569
|
+
### Explicit navigation links:
|
|
570
|
+
|
|
571
|
+
However for tricky UIs you can manually bind navigation links for containers:
|
|
572
|
+
|
|
573
|
+
```ts
|
|
574
|
+
button1.navigationLinks.up = button2;
|
|
575
|
+
```
|
|
576
|
+
|
|
515
577
|
### How it works
|
|
516
578
|
|
|
517
579
|
The Navigation API is centered around the **UINavigation** manager, which
|
|
@@ -526,24 +588,14 @@ responsible for asking the **first responder** whether it can handle the intent.
|
|
|
526
588
|
If it returns `false`, any other responders are checked (if they exist),
|
|
527
589
|
otherwise the default global navigation behavior kicks in.
|
|
528
590
|
|
|
529
|
-
### Default Global Navigation Behaviors
|
|
530
|
-
|
|
531
|
-
When a navigation action is **not** handled manually by a responder, it is handled in one of the following ways:
|
|
532
|
-
|
|
533
|
-
| Action | Behavior |
|
|
534
|
-
|---|---|
|
|
535
|
-
|`"NavigateBack"`|<ul><li>No action.</li></ul>|
|
|
536
|
-
|`"NavigateLeft"`, `"NavigateRight"`, `"NavigateUp"`, `"NavigateDown"`|<ul><li>Looks for the nearest `Container` where `container.isNavigatable` in the direction given, and if found, receives a `"pointerover"` event.</li><li>Additionally, if the newly focused container has registered an event handler for either `"pointerover"`, it will fire that too.</li><li>If we were previously focused on a container, that previous container receives a `"pointerout"` event.</li></ul>|
|
|
537
|
-
|`"NavigateActivate"`|<ul><li>Checks if we are currently focused on a container, and then issue a `"pointerdown"` and `"pointerup"` event.</li><li>If the focused container has registered an event handler for either `"pointerdown"`, that event handler will be fired too.</li></ul>|
|
|
538
|
-
|
|
539
591
|
### Container Navigatability
|
|
540
592
|
|
|
541
593
|
Containers are extended with a few properties/accessors:
|
|
542
594
|
|
|
543
595
|
| Container properties | type | default | description
|
|
544
596
|
|---------------------|------|---------|--------------
|
|
545
|
-
| `
|
|
546
|
-
| `navigationMode` | `"auto"` \| `"
|
|
597
|
+
| `navigatable` | `readonly boolean` | `false` | returns `true` if `navigationMode` is set to `"always"`, |or is `"auto"` and is interactive with at least one of "pointertap", "pointerup" or "pointerdown".
|
|
598
|
+
| `navigationMode` | `"auto"` \| `"always"` | `"none"` \| `"auto"` | When set to `"auto"`, a `Container` can be navigated to if it is int
|
|
547
599
|
| `navigationPriority` | `number` | `0` | The priority relative to other navigation items in this group.
|
|
548
600
|
|
|
549
601
|
### Default Binds
|
|
@@ -599,30 +651,63 @@ for (const device of InputDevice.devices)
|
|
|
599
651
|
You can easily map an on-screen input device using the `CustomDevice` interface.
|
|
600
652
|
|
|
601
653
|
```ts
|
|
602
|
-
|
|
603
|
-
id = "onscreen"
|
|
604
|
-
type = "custom" as const
|
|
605
|
-
meta: Record<string, any> = {}
|
|
654
|
+
import { CustomDevice, NamedBind } from "pixijs-input-devices";
|
|
606
655
|
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
656
|
+
export class VirtualInputDevice extends Container implements CustomDevice
|
|
657
|
+
{
|
|
658
|
+
type = "custom" as const;
|
|
659
|
+
id = "virtual-gamepad";
|
|
660
|
+
meta = {};
|
|
661
|
+
|
|
662
|
+
// example game input:
|
|
663
|
+
input = { moveX: 0, jump: false };
|
|
664
|
+
|
|
665
|
+
// views:
|
|
666
|
+
joystick = new GameJoystick({ parent: this, x: -200 });
|
|
667
|
+
button1 = new GameButton({ parent: this, x: 300 });
|
|
611
668
|
|
|
612
669
|
update(now)
|
|
613
670
|
{
|
|
614
|
-
this.
|
|
615
|
-
this.
|
|
671
|
+
this.input.moveX = this.joystick.x;
|
|
672
|
+
this.input.jump = this.button1.pressed;
|
|
616
673
|
}
|
|
617
674
|
|
|
618
|
-
//
|
|
619
|
-
bindDown(name)
|
|
675
|
+
// (Optional) integrate named binds
|
|
676
|
+
bindDown(name: NamedBind): boolean
|
|
677
|
+
{
|
|
678
|
+
switch (name) {
|
|
679
|
+
case "Jump":
|
|
680
|
+
return this.button1.pressed;
|
|
681
|
+
|
|
682
|
+
case "Left":
|
|
683
|
+
return this.joystick.x < 0.5;
|
|
684
|
+
|
|
685
|
+
case "Right":
|
|
686
|
+
return this.joystick.x > 0.5;
|
|
687
|
+
|
|
688
|
+
default:
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
620
692
|
}
|
|
621
693
|
|
|
622
|
-
const
|
|
694
|
+
const myDevice = new VirtualInputDevice();
|
|
695
|
+
|
|
696
|
+
// enable device
|
|
697
|
+
InputDevice.add(myDevice);
|
|
698
|
+
myDevice.on("destroyed", () => InputDevice.remove(myVirtualDevice))
|
|
699
|
+
|
|
700
|
+
// (Optional) participate in named binds, including navigation:
|
|
701
|
+
myDevice.joystick
|
|
702
|
+
.on("pointermove", debounce(50, () => {
|
|
703
|
+
if (myDevice.joystick.x < 0.5) InputDevice.emitBind("NavigateLeft", myDevice);
|
|
704
|
+
if (myDevice.joystick.x > 0.5) InputDevice.emitBind("NavigateRight", myDevice);
|
|
705
|
+
if (myDevice.joystick.y < 0.5) InputDevice.emitBind("NavigateUp", myDevice);
|
|
706
|
+
if (myDevice.joystick.y > 0.5) InputDevice.emitBind("NavigateDown", myDevice);
|
|
707
|
+
}));
|
|
623
708
|
|
|
624
|
-
|
|
625
|
-
InputDevice.
|
|
709
|
+
myDevice.button1
|
|
710
|
+
.on("pointertap", () => InputDevice.emitBind("NavigateActivate", myDevice));
|
|
626
711
|
```
|
|
627
712
|
|
|
628
713
|
### Two Users; One Keyboard
|
|
@@ -631,45 +716,45 @@ You could set up multiple named inputs:
|
|
|
631
716
|
|
|
632
717
|
```ts
|
|
633
718
|
InputDevice.keyboard.configureBinds({
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
719
|
+
Jump : [ "ArrowUp", "KeyW" ],
|
|
720
|
+
Crouch : [ "ArrowDown", "KeyS" ],
|
|
721
|
+
Left : [ "ArrowLeft", "KeyA" ],
|
|
722
|
+
Right : [ "ArrowRight", "KeyD" ],
|
|
723
|
+
|
|
724
|
+
P1_Jump : [ "KeyW" ],
|
|
725
|
+
P1_Crouch : [ "KeyS" ],
|
|
726
|
+
P1_Left : [ "KeyA" ],
|
|
727
|
+
P1_Right : [ "KeyD" ],
|
|
728
|
+
|
|
729
|
+
P2_Jump : [ "ArrowUp" ],
|
|
730
|
+
P2_Crouch : [ "ArrowDown" ],
|
|
731
|
+
P2_Left : [ "ArrowLeft" ],
|
|
732
|
+
P2_Right : [ "ArrowRight" ]
|
|
648
733
|
})
|
|
649
734
|
```
|
|
650
735
|
|
|
651
|
-
and then switch groups depending on the mode:
|
|
736
|
+
and then switch groups depending on the mode/player:
|
|
652
737
|
|
|
653
738
|
```ts
|
|
654
|
-
if (
|
|
739
|
+
if (SINGLE_PLAYER_MODE)
|
|
655
740
|
{
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
741
|
+
player.jump = device.bindDown("Jump");
|
|
742
|
+
player.defend = device.bindDown("Crouch");
|
|
743
|
+
player.moveX += device.bindDown("Left") ? -1 : 0;
|
|
744
|
+
player.moveX += device.bindDown("Right") ? 1 : 0;
|
|
745
|
+
}
|
|
746
|
+
else if (player.id === 1)
|
|
747
|
+
{
|
|
748
|
+
player.jump = device.bindDown("P1_Jump");
|
|
749
|
+
player.defend = device.bindDown("P1_Crouch");
|
|
750
|
+
player.moveX += device.bindDown("P1_Left") ? -1 : 0;
|
|
751
|
+
player.moveX += device.bindDown("P1_Right") ? 1 : 0;
|
|
665
752
|
}
|
|
666
753
|
else
|
|
667
754
|
{
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
updateComputerPlayerInput(player2)
|
|
755
|
+
player.jump = device.bindDown("P2_Jump");
|
|
756
|
+
player.defend = device.bindDown("P2_Crouch");
|
|
757
|
+
player.moveX += device.bindDown("P2_Left") ? -1 : 0;
|
|
758
|
+
player.moveX += device.bindDown("P2_Right") ? 1 : 0;
|
|
674
759
|
}
|
|
675
760
|
```
|