pixijs-input-devices 0.1.2 â 0.2.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 +161 -521
- package/dist/index.cjs +1 -354
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +372 -211
- package/dist/index.mjs +1 -354
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,578 +1,218 @@
|
|
|
1
|
-
# đšī¸ pixijs-input-devices  
|
|
1
|
+
# đšī¸ pixijs-input-devices  [](https://www.npmjs.com/package/pixijs-input-devices) [](https://bundlephobia.com/package/pixijs-input-devices) [](https://www.npmjs.com/package/pixijs-input-devices) [](https://github.com/reececomo/pixijs-input-devices/actions/workflows/tests.yml) [](https://github.com/reececomo/pixijs-input-devices/blob/main/LICENSE)
|
|
2
2
|
|
|
3
3
|
WIP
|
|
4
4
|
|
|
5
|
-
- Adds support for â¨ī¸
|
|
6
|
-
-
|
|
7
|
-
-
|
|
5
|
+
- Adds support for â¨ī¸ **Keyboard**, đŽ **Gamepads**, and other human-interface devices
|
|
6
|
+
- A simple `Navigation` API which hooks devices into existing pointer/mouse events
|
|
7
|
+
- A powerful event-based API for event-driven interactions
|
|
8
|
+
- and of course, a high-performance API for real-time applications
|
|
8
9
|
|
|
9
10
|
<hr/>
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
// set up ticker (or put in your main update loop):
|
|
13
|
-
Ticker.shared.add( () => InputDeviceManager.shared.update() );
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
### Realtime API (polling)
|
|
17
|
-
|
|
18
|
-
Simple example
|
|
19
|
-
|
|
20
|
-
```ts
|
|
21
|
-
let jump = false;
|
|
22
|
-
let hide = false;
|
|
23
|
-
let moveX = 0.0;
|
|
24
|
-
|
|
25
|
-
for ( const device of InputDeviceManager.shared.devices )
|
|
26
|
-
{
|
|
27
|
-
if ( device.type === "keyboard" )
|
|
28
|
-
{
|
|
29
|
-
if ( device.key["ArrowUp"] ) jump = true
|
|
30
|
-
if ( device.key["ArrowDown"] ) hide = true
|
|
31
|
-
if ( device.key["ArrowLeft"] ) moveX = -1.0
|
|
32
|
-
if ( device.key["ArrowRight"] ) moveX = 1.0
|
|
33
|
-
}
|
|
34
|
-
else if ( device.type === "gamepad" )
|
|
35
|
-
{
|
|
36
|
-
if ( device.button[Button.A] ) jump = true
|
|
37
|
-
if ( device.rightTrigger >= 0.25 ) hide = true
|
|
38
|
-
if ( device.leftJoystick.x !== 0.0 ) moveX = device.leftJoystick.x
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
> [!NOTE]
|
|
44
|
-
> You can use `device.button[Button.RightTrigger]` to detect triggers too.
|
|
45
|
-
|
|
46
|
-
> [!NOTE]
|
|
47
|
-
> You can use `device.pressingAll([...])` and `device.pressingAny([...])` to check groups of keys/buttons.
|
|
48
|
-
|
|
49
|
-
> [!NOTE]
|
|
50
|
-
> You can use `device.assignee` to store any device assignment-related data (such as a user ID).
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
### Event-driven API
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
InputDeviceManager.shared.keyboard.bindGlobal({
|
|
57
|
-
{ key: "KeyA", action}
|
|
58
|
-
})
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
By default, any element with "mousedown" or "pointerdown" will
|
|
62
|
-
|
|
63
|
-
Container events | description
|
|
64
|
-
------------------|--------------------------------------------------------
|
|
65
|
-
`focus` | Target became focused. args: direction: { x, y }
|
|
66
|
-
`blur` | Target lost focus. args: direction: { x, y }
|
|
67
|
-
`focusHinted` | Target may be about to become focused. args: direction: { x, y }
|
|
68
|
-
`blurHinted` | Target may be about to lose focus. args: direction: { x, y }
|
|
69
|
-
|
|
70
|
-
Container properties | description
|
|
71
|
-
---------------------|-----------------------------------------------------
|
|
72
|
-
`focusable` | boolean?, default: `undefined`
|
|
73
|
-
`isFocusGroup` | boolean?, default: `false`
|
|
74
|
-
`focusPriority` | number?, default: `0`
|
|
75
|
-
`focusGroup` | PIXI.Container?, default: `app.stage ?? undefined`
|
|
76
|
-
`focusTransform` | `{ x, y }`, default: `{ x: 5, y: 5 }`
|
|
77
|
-
`isFocusable` | get () => boolean, returns true if "focusable" = true, or InputDevice.defaultOptions.autoFocusable and has "mousedown", "pointerdown"
|
|
78
|
-
|
|
79
|
-
Device gesture | Keyboard (primary) | Keyboard (alt) | Gamepad
|
|
80
|
-
-----------------|--------------------|-----------------|------------------
|
|
81
|
-
`"moveFocusLeft"` | Left Arrow Key | A Key | Left Stick Left
|
|
82
|
-
`"moveFocusRight"` | Right Arrow Key | D Key | Left Stick Right
|
|
83
|
-
`"moveFocusUp"` | Up Arrow Key | W Key | Left Stick Up
|
|
84
|
-
`"moveFocusDown"` | Down Arrow Key | S Key | Left Stick Down
|
|
85
|
-
`"focusover"` | Return Key | Space | A Button
|
|
86
|
-
`"moveUpOneLevel"` | Escape Key | Backspace | B Button
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
### Global defaults:
|
|
90
|
-
|
|
91
|
-
```ts
|
|
92
|
-
InputDevice.defaultOptions = {
|
|
93
|
-
/** Globally enable/disable the navigation API. (i.e. if the browser loses focus). */
|
|
94
|
-
focusEnabled: true,
|
|
95
|
-
/**
|
|
96
|
-
* (default: true) When enabled, any element with "mousedown" or "pointerdown"
|
|
97
|
-
* is considered focusable. The "focusableTransform" will also apply.
|
|
98
|
-
*/
|
|
99
|
-
autoFocusable: true,
|
|
100
|
-
focusableTransform: { x: 5, y: 5 },
|
|
101
|
-
keyboard: {
|
|
102
|
-
enabled: true,
|
|
103
|
-
layouts: [ "qwerty", "azerty", "jcuken", "qwertz" ],
|
|
104
|
-
autoLanguageLayout: true, // fr*/nl-BE* -> azerty, uk*/ru* -> jcuken
|
|
105
|
-
internationalization: {
|
|
106
|
-
/** when true, labels are mapped to their layout */
|
|
107
|
-
mapLabels: true,
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
gamepad: {
|
|
111
|
-
enabled: true,
|
|
112
|
-
layouts: [ "standard", "nintendo" ],
|
|
113
|
-
firstIntentCooldownMs: 400,
|
|
114
|
-
intentCooldownMs: 100,
|
|
115
|
-
},
|
|
116
|
-
}
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
```ts
|
|
120
|
-
class InputDevice {
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
<hr/>
|
|
128
|
-
|
|
129
|
-
<hr/>
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
⥠Powerful, high-performance animations for PixiJS
|
|
133
|
-
|
|
134
|
-
| | |
|
|
135
|
-
| ------ | ------ |
|
|
136
|
-
| đŽ Simple, declarative API | đŦ Based on [Cocos2d](https://docs.cocos2d-x.org/cocos2d-x/v3/en/actions/getting_started.html)/[SKActions](https://developer.apple.com/documentation/spritekit/getting_started_with_actions) |
|
|
137
|
-
| đ 35+ [built-in actions](#action-initializers), 30+ [timing modes](#timing-modes) | đ Reuseable, chainable & reversible |
|
|
138
|
-
| đ No dependencies & tree-shakeable | â Full speed/pausing control |
|
|
139
|
-
| đ¤ `~4.3kb` minzipped | ⨠Supports PixiJS 8+, 7+, 6.3+ |
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
## Sample Usage
|
|
143
|
-
|
|
144
|
-
*Create, configure, and run animations & actions.*
|
|
145
|
-
|
|
146
|
-
```ts
|
|
147
|
-
// Define an action
|
|
148
|
-
const spinAndRemove = Action.sequence([
|
|
149
|
-
Action.rotateByDegrees(360, 0.2).easeInOut(),
|
|
150
|
-
Action.fadeOut(0.2).easeIn(),
|
|
151
|
-
Action.removeFromParent(),
|
|
152
|
-
Action.run(() => console.info('⨠done!'))
|
|
153
|
-
]);
|
|
154
|
-
|
|
155
|
-
// Run an action
|
|
156
|
-
mySprite.run(spinAndRemove);
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
## Getting Started with PixiJS Actions
|
|
160
|
-
|
|
161
|
-
*Everything you need to quickly build beautiful animations.*
|
|
162
|
-
|
|
163
|
-
**PixiJS Actions** is based off the idiomatic and expressive [**SKActions API**](https://developer.apple.com/documentation/spritekit/getting_started_with_actions), extending `Container` to add first-class support for running and managing actions.
|
|
164
|
-
|
|
165
|
-
The core concepts are:
|
|
166
|
-
|
|
167
|
-
1. **Nodes:** _Any container (e.g. `Container`, `Sprite`, `Graphics`)_
|
|
168
|
-
2. **Actions:** _Stateless, reusable recipes_ (e.g. animations, triggers, and more)
|
|
169
|
-
3. **TimingMode & speed:** _Controls for the speed & smoothness of actions and animations_
|
|
170
|
-
|
|
171
|
-
> [!NOTE]
|
|
172
|
-
> _See [Timing Modes](#timing-modes) and [Manipulating Action Speed](#manipulating-action-speed) for more information._
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
## Installation
|
|
176
|
-
|
|
177
|
-
*Quick start guide.*
|
|
178
|
-
|
|
179
|
-
**1.** Install the latest `pixijs-input-devices` package:
|
|
12
|
+
### đŋ Install
|
|
180
13
|
|
|
181
14
|
```sh
|
|
182
|
-
|
|
183
|
-
npm install pixijs-input-devices -D
|
|
184
|
-
|
|
185
|
-
# yarn
|
|
186
|
-
yarn add pixijs-input-devices --dev
|
|
15
|
+
npm i pixijs-input-devices
|
|
187
16
|
```
|
|
188
17
|
|
|
189
|
-
|
|
18
|
+
#### Setup
|
|
190
19
|
|
|
191
20
|
```ts
|
|
192
|
-
import
|
|
193
|
-
import { Action, registerPixiJSActionsMixin } from 'pixijs-input-devices';
|
|
194
|
-
|
|
195
|
-
// register container mixin
|
|
196
|
-
registerPixiJSActionsMixin(PIXI.Container);
|
|
21
|
+
import { InputDevice, Navigation } from "pixijs-input-devices"
|
|
197
22
|
|
|
198
|
-
// register
|
|
199
|
-
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
> [!TIP]
|
|
203
|
-
> **PixiJS 7 / 6.3+:**
|
|
204
|
-
>
|
|
205
|
-
> ```ts
|
|
206
|
-
> Ticker.shared.add(() => Action.tick(Ticker.shared.elapsedMS));
|
|
207
|
-
> // or
|
|
208
|
-
> Ticker.shared.add((dt) => Action.tick(dt));
|
|
209
|
-
> ```
|
|
210
|
-
|
|
211
|
-
> [!NOTE]
|
|
212
|
-
> _If not using a PixiJS ticker, then just put `Action.tick(elapsedMs)` in the appropriate equivalent place (i.e. your `requestAnimationFrame()` render loop)._
|
|
213
|
-
|
|
214
|
-
**3.** Done!
|
|
215
|
-
|
|
216
|
-
⨠You are now ready to run your first action!
|
|
23
|
+
// register mixin
|
|
24
|
+
registerPixiJSInputDeviceMixin( Container )
|
|
217
25
|
|
|
218
|
-
|
|
26
|
+
// add update loop
|
|
27
|
+
Ticker.shared.add( () => InputDevice.update() )
|
|
219
28
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
```ts
|
|
223
|
-
// Hide me instantly!
|
|
224
|
-
mySprite.run(Action.hide(), () => {
|
|
225
|
-
console.log('where did I go?');
|
|
226
|
-
});
|
|
29
|
+
// enable nav
|
|
30
|
+
Navigation.stage = app.stage
|
|
227
31
|
```
|
|
228
32
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
| Property | Description |
|
|
232
|
-
| :----- | :------ |
|
|
233
|
-
| `speed` | A speed modifier applied to all actions executed by the node and its descendants. Defaults to `1.0`. |
|
|
234
|
-
| `isPaused` | A boolean value that determines whether actions on the node and its descendants are processed. Defaults to `false`. |
|
|
235
|
-
|
|
236
|
-
| Method | Description |
|
|
237
|
-
| :----- | :------ |
|
|
238
|
-
| `run(action)` | Run an action. |
|
|
239
|
-
| `run(action, completion)` | Run an action with a completion handler. |
|
|
240
|
-
| `runWithKey(action, withKey)` | Run an action, and store it so it can be retrieved later. |
|
|
241
|
-
| `runAsPromise(action): Promise<void>` | Run an action as a promise. |
|
|
242
|
-
| `action(forKey): Action \| undefined` | Return an action associated with a specified key. |
|
|
243
|
-
| `hasActions(): boolean` | Return a boolean indicating whether the node is executing actions. |
|
|
244
|
-
| `removeAllActions(): void` | End and removes all actions from the node. |
|
|
245
|
-
| `removeAction(forKey): void` | Remove an action associated with a specified key. |
|
|
33
|
+
### Realtime API
|
|
246
34
|
|
|
247
|
-
|
|
35
|
+
Simple example:
|
|
248
36
|
|
|
249
37
|
```ts
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
38
|
+
let jump = false,
|
|
39
|
+
hide = false,
|
|
40
|
+
moveX = 0.0;
|
|
253
41
|
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
42
|
+
// keyboard:
|
|
43
|
+
const kb = InputDevice.keyboard;
|
|
44
|
+
if ( kb.key.ArrowUp ) jump = true
|
|
45
|
+
if ( kb.key.ArrowDown ) hide = true
|
|
46
|
+
if ( kb.key.ArrowLeft ) moveX = -1.0
|
|
47
|
+
else if ( kb.key.ArrowRight ) moveX = 1.0
|
|
259
48
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
Speed can be manipulated on both actions and nodes.
|
|
268
|
-
|
|
269
|
-
```ts
|
|
270
|
-
const moveAction = Action.moveByX(10, 4.0);
|
|
271
|
-
moveAction.speed = 2.0;
|
|
272
|
-
// moveAction will now take only 2 seconds instead of 4.
|
|
273
|
-
|
|
274
|
-
const repeatAction = Action.repeat(moveAction, 5);
|
|
275
|
-
repeatAction.speed = 2.0;
|
|
276
|
-
// Each moveAction will only take 1 second, for a total of 5 seconds.
|
|
277
|
-
|
|
278
|
-
mySprite.run(moveAction);
|
|
279
|
-
mySprite.speed = 2.0;
|
|
280
|
-
// mySprite is running at 2x speed!
|
|
281
|
-
// The entire action should now take ~2.5 seconds.
|
|
282
|
-
|
|
283
|
-
mySprite.parent!.speed = 1 / 4;
|
|
284
|
-
// Now we've slowed down mySprite's parent.
|
|
285
|
-
// The entire action will now take ~10 seconds.
|
|
49
|
+
// gamepads:
|
|
50
|
+
for ( const gamepad of InputDevice.gamepads )
|
|
51
|
+
{
|
|
52
|
+
if ( gamepad.button.A ) jump = true
|
|
53
|
+
if ( gamepad.rightTrigger > 0.25 ) hide = true
|
|
54
|
+
if ( gamepad.leftJoystick.x !== 0.0 ) moveX = gamepad.leftJoystick.x
|
|
55
|
+
}
|
|
286
56
|
```
|
|
287
57
|
|
|
288
|
-
|
|
289
|
-
> Changes to nodes' `speed` will take effect immediately, however changes to an `Action` initializer's `speed` or `timingMode` will not affect any actions that have already begun running.
|
|
290
|
-
|
|
291
|
-
## Action Initializers
|
|
292
|
-
|
|
293
|
-
*Combine these initializers to create expressive animations and behaviors.*
|
|
294
|
-
|
|
295
|
-
Most actions implement specific predefined animations that are ready to use. If your animation needs fall outside of the suite provided here, then you should implement a custom action (see [Creating Custom Actions](#creating-custom-actions)).
|
|
296
|
-
|
|
297
|
-
| Action | Description | Reversible? |
|
|
298
|
-
| :----- | :---------- | :---------- |
|
|
299
|
-
|***Chaining Actions***|||
|
|
300
|
-
| `Action.group(actions)` | Run multiple actions in parallel. | Yes |
|
|
301
|
-
| `Action.sequence(actions)` | Run multiple actions sequentially. | Yes |
|
|
302
|
-
| `Action.repeat(action, count)` | Repeat an action a specified number of times. | Yes |
|
|
303
|
-
| `Action.repeatForever(action)` | Repeat an action indefinitely. | Yes |
|
|
304
|
-
|***Animating a Node's Position in a Linear Path***|||
|
|
305
|
-
| `Action.moveBy(vector, duration)` | Move a node by a relative vector `{ x, y }` (e.g. `PIXI.Point`). | Yes |
|
|
306
|
-
| `Action.moveBy(dx, dy, duration)` | Move a node by relative values. | Yes |
|
|
307
|
-
| `Action.moveByX(dx, duration)` | Move a node horizontally by a relative value. | Yes |
|
|
308
|
-
| `Action.moveByY(dy, duration)` | Move a node vertically by a relative value. | Yes |
|
|
309
|
-
| `Action.moveTo(position, duration)` | Move a node to a specified position `{ x, y }` (e.g. `PIXI.Point`, `PIXI.Container`). | _*No_ |
|
|
310
|
-
| `Action.moveTo(x, y, duration)` | Move a node to a specified position. | _*No_ |
|
|
311
|
-
| `Action.moveToX(x, duration)` | Move a node to a specified horizontal position. | _*No_ |
|
|
312
|
-
| `Action.moveToY(y, duration)` | Move a node to a specified vertical position. | _*No_ |
|
|
313
|
-
|***Animating a Node's Position Along a Custom Path***|||
|
|
314
|
-
| `Action.follow(path, duration)` | Move a node along a path, optionally orienting the node to the path. | Yes | Yes |
|
|
315
|
-
| `Action.followAtSpeed(path, speed)` | Move a node along a path at a specified speed, optionally orienting the node to the path. | Yes |
|
|
316
|
-
|***Animating the Rotation of a Node***|||
|
|
317
|
-
| `Action.rotateBy(delta, duration)` | Rotate a node by a relative value (in radians). | Yes |
|
|
318
|
-
| `Action.rotateByDegrees(delta, duration)` | Rotate a node by a relative value (in degrees). | Yes |
|
|
319
|
-
| `Action.rotateTo(radians, duration)` | Rotate a node to a specified value (in radians). | _*No_ |
|
|
320
|
-
| `Action.rotateToDegrees(degrees, duration)` | Rotate a node to a specified value (in degrees). | _*No_ |
|
|
321
|
-
|***Animating the Scaling of a Node***|||
|
|
322
|
-
| `Action.scaleBy(vector, duration)` | Scale a node by a relative vector `{ x, y }` (e.g. `PIXI.Point`). | Yes |
|
|
323
|
-
| `Action.scaleBy(scale, duration)` | Scale a node by a relative value. | Yes |
|
|
324
|
-
| `Action.scaleBy(dx, dy, duration)` | Scale a node in each axis by relative values. | Yes |
|
|
325
|
-
| `Action.scaleByX(dx, duration)` | Scale a node horizontally by a relative value. | Yes |
|
|
326
|
-
| `Action.scaleByY(dy, duration)` | Scale a node vertically by a relative value. | Yes |
|
|
327
|
-
| `Action.scaleTo(size, duration)` | Scale a node to achieve a specified size `{ width, height }` (e.g. `PIXI.ISize`, `PIXI.Container`). | _*No_ |
|
|
328
|
-
| `Action.scaleTo(scale, duration)` | Scale a node to a specified value. | _*No_ |
|
|
329
|
-
| `Action.scaleTo(x, y, duration)` | Scale a node in each axis to specified values. | _*No_ |
|
|
330
|
-
| `Action.scaleToX(x, duration)` | Scale a node horizontally to a specified value. | _*No_ |
|
|
331
|
-
| `Action.scaleToY(y, duration)` | Scale a node vertically to a specified value. | _*No_ |
|
|
332
|
-
|***Animating the Transparency of a Node***|||
|
|
333
|
-
| `Action.fadeIn(duration)` | Fade the alpha to `1.0`. | Yes |
|
|
334
|
-
| `Action.fadeOut(duration)` | Fade the alpha to `0.0`. | Yes |
|
|
335
|
-
| `Action.fadeAlphaBy(delta, duration)` | Fade the alpha by a relative value. | Yes |
|
|
336
|
-
| `Action.fadeAlphaTo(alpha, duration)` | Fade the alpha to a specified value. | _*No_ |
|
|
337
|
-
|***Controlling a Node's Visibility***|||
|
|
338
|
-
| `Action.unhide()` | Set a node's `visible` property to `true`. | Yes |
|
|
339
|
-
| `Action.hide()` | Set a node's `visible` property to `false`. | Yes |
|
|
340
|
-
|***Removing a Node from the Canvas***|||
|
|
341
|
-
| `Action.removeFromParent()` | Remove a node from its parent. | _â Identical_ |
|
|
342
|
-
|***Running Actions on Children***|||
|
|
343
|
-
| `Action.runOnChild(nameOrLabel, action)` | Add an action to a child node. | Yes |
|
|
344
|
-
|***Delaying Actions***|||
|
|
345
|
-
| `Action.waitForDuration(duration)` | Idle for a specified period of time. | _â Identical_ |
|
|
346
|
-
| `Action.waitForDurationWithRange(duration, range)` | Idle for a randomized period of time. | _â Identical_ |
|
|
347
|
-
|***Triggers and Custom Actions***|||
|
|
348
|
-
| `Action.run(callback)` | Execute a block (i.e. trigger another action). | _â Identical_ |
|
|
349
|
-
| `Action.customAction(duration, stepHandler)` | Execute a custom stepping function over the action duration. | _â Identical_ |
|
|
350
|
-
|***Manipulating the Action Speed of a Node***|||
|
|
351
|
-
| `Action.speedBy(delta, duration)` | Change how fast a node executes its actions by a relative value. | Yes |
|
|
352
|
-
| `Action.speedTo(speed, duration)` | Set how fast a node executes actions to a specified value. | _*No_ |
|
|
353
|
-
|
|
354
|
-
> [!IMPORTANT]
|
|
355
|
-
> #### Reversing Actions
|
|
356
|
-
> Every action initializer has a `.reversed()` method which will return a new action. Some actions are **not reversible**, and these cases are noted in the table above:
|
|
357
|
-
> - _**â Identical**_ — The reversed action is identical to the original action.
|
|
358
|
-
> - _**\*No**_ — The reversed action will idle for the equivalent duration.
|
|
359
|
-
|
|
360
|
-
### Action Chaining
|
|
361
|
-
|
|
362
|
-
Many actions can be joined together using `Action.sequence()`, `Action.group()`, `Action.repeat()` and `Action.repeatForever()` to quickly create complex animations:
|
|
58
|
+
### Event API
|
|
363
59
|
|
|
364
60
|
```ts
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
Action.scaleTo(1.5, 1.0).easeOut(),
|
|
370
|
-
Action.scaleTo(1, 1.0).easeIn()
|
|
371
|
-
]);
|
|
372
|
-
|
|
373
|
-
// Follow a complex path (e.g. a bezier curve)
|
|
374
|
-
const path = [
|
|
375
|
-
{ x: 0, y: 0 },
|
|
376
|
-
{ x: 100, y: 0 },
|
|
377
|
-
{ x: 100, y: 100 },
|
|
378
|
-
{ x: 200, y: 200 }
|
|
379
|
-
];
|
|
380
|
-
const followPath = Action.follow(path, 5.0);
|
|
381
|
-
|
|
382
|
-
// Create a 10 second loop that goes back and forth
|
|
383
|
-
const moveBackAndForthWhilePulsating = Action.group([
|
|
384
|
-
Action.repeat(pulsate, 5),
|
|
385
|
-
Action.sequence([followPath, followPath.reversed()]),
|
|
386
|
-
]);
|
|
387
|
-
|
|
388
|
-
// ⨠Animate continuously
|
|
389
|
-
mySprite.run(Action.repeatForever(moveBackAndForthWhilePulsating));
|
|
390
|
-
```
|
|
391
|
-
|
|
392
|
-
## Timing Modes
|
|
393
|
-
|
|
394
|
-
Every action has a `timingMode` which controls the timing curve of its execution.
|
|
61
|
+
// listen to device events
|
|
62
|
+
InputDevice.on( "deviceconnected", ({ device }) => {
|
|
63
|
+
console.debug( "A new " + device.type + " device connected!" ) // "keyboard" | "gamepad" | "custom"
|
|
64
|
+
})
|
|
395
65
|
|
|
396
|
-
|
|
66
|
+
// keyboard events
|
|
67
|
+
InputDevice.keyboard.on( "layoutdetected", ({ layout }) => {
|
|
68
|
+
console.debug( "layout detected as " + layout ); // "JCUKEN" | "AZERTY" | "QWERTZ" | "QWERTY"
|
|
69
|
+
})
|
|
70
|
+
InputDevice.keyboard.on( "Escape", () => showMenu() )
|
|
397
71
|
|
|
398
|
-
|
|
72
|
+
// gamepad events
|
|
73
|
+
InputDevice.gamepads[0].on( Button.Back, () => showMenu() )
|
|
74
|
+
InputDevice.gamepads[0].on( "Back", () => showMenu() )
|
|
399
75
|
|
|
400
|
-
```
|
|
401
|
-
// Default easings:
|
|
402
|
-
Action.fadeIn(0.3).easeIn();
|
|
403
|
-
Action.fadeIn(0.3).easeOut();
|
|
404
|
-
Action.fadeIn(0.3).easeInOut();
|
|
76
|
+
```
|
|
405
77
|
|
|
406
|
-
|
|
407
|
-
Action.fadeIn(0.3).setTimingMode(TimingMode.easeInOutCubic);
|
|
78
|
+
#### InputAction Events
|
|
408
79
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
80
|
+
| Event | Description | Payload |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `"deviceconnected"` | `{device}` | A device has become available. |
|
|
83
|
+
| `"devicedisconnected"` | `{device}` | A device has been removed. |
|
|
412
84
|
|
|
413
|
-
|
|
414
|
-
> **Timing Mutators:** The `.easeIn()`, `.easeOut()`, `.easeInOut()`, `setTimingMode(âĻ)`, `setSpeed(âĻ)` methods mutate the underlying action.
|
|
85
|
+
#### Keyboard Events
|
|
415
86
|
|
|
416
|
-
|
|
87
|
+
| Event | Description | Payload |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `"layoutdetected"` | `{layout,layoutSource,device}` | The keyboard layout (`"QWERTY"`, `"QWERTZ"`, `"AZERTY"`, or `"JCUKEN"`) has been detected, either from the native API or from keypresses. |
|
|
90
|
+
| **Key presses:** | | |
|
|
91
|
+
| `"KeyA"` \| `"KeyB"` \| ... 103 more ... | `{event,keyCode,keyLabel,device}` | `"KeyA"` was pressed. |
|
|
417
92
|
|
|
418
|
-
|
|
93
|
+
#### Gamepad Events
|
|
419
94
|
|
|
420
|
-
|
|
|
421
|
-
|
|
422
|
-
| **
|
|
423
|
-
|
|
|
424
|
-
|
|
|
425
|
-
| **
|
|
426
|
-
|
|
|
427
|
-
|
|
|
428
|
-
| **Quintic** | `easeInOutQuint` | `easeInQuint` | `easeOutQuint` | Very gradual start and end, smoother acceleration in the middle. |
|
|
429
|
-
| **Exponential** | `easeInOutExpo` | `easeInExpo` | `easeOutExpo` | Very slow start, exponential acceleration, slow end. |
|
|
430
|
-
| **Back** | `easeInOutBack` | `easeInBack` | `easeOutBack` | Starts slowly, overshoots slightly, settles into final position. |
|
|
431
|
-
| **Bounce** | `easeInOutBounce` | `easeInBounce` | `easeOutBounce` | Bouncy effect at the start or end, with multiple rebounds. |
|
|
432
|
-
| **Elastic** | `easeInOutElastic` | `easeInElastic` | `easeOutElastic` | Stretchy motion with overshoot and multiple oscillations. |
|
|
95
|
+
| Event | Description | Payload |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| **Button presses:** | | |
|
|
98
|
+
| `"A"` \| `"B"` \| `"X"` \| ... 13 more ... | `{button,buttonCode,device}` | Button `"A"` was pressed. Equivalent to `0`. |
|
|
99
|
+
| ... | ... | ... |
|
|
100
|
+
| **Button presses (no label):** | | |
|
|
101
|
+
| `0` \| `1` \| `2` \| ... 13 more ... | `{button,buttonCode,device}` | Button `0` was pressed. Equivalent to `"A"`. |
|
|
102
|
+
| ... | ... | ... |
|
|
433
103
|
|
|
434
|
-
|
|
104
|
+
> [!TIP]
|
|
105
|
+
> **Multiplayer:** For multiple players, consider assigning devices
|
|
106
|
+
> using `device.meta` (e.g. `device.meta.player = 1`) and use
|
|
107
|
+
> `InputDevice.devices` to iterate through devices instead.
|
|
435
108
|
|
|
436
|
-
|
|
109
|
+
##### Gamepad Layouts
|
|
437
110
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
|
442
|
-
|
|
443
|
-
| `
|
|
111
|
+
> [!NOTE]
|
|
112
|
+
> **Gamepad Labels:** The gamepad buttons are aliased with generic standard controller buttons in a Logitech/Xbox/Steam controller layout.
|
|
113
|
+
|
|
114
|
+
| Button | ButtonCode | Name | Generic | Nintendo<br/>(*physical) | Playstation | Xbox |
|
|
115
|
+
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
116
|
+
| `0` | `"A"` | **A** | A | B | Cross | A |
|
|
117
|
+
| `1` | `"B"` | **B** | B | A | Circle | B |
|
|
118
|
+
| `2` | `"X"` | **X** | X | Y | Square | X |
|
|
119
|
+
| `3` | `"Y"` | **Y** | Y | X | Triangle | Y |
|
|
120
|
+
| `4` | `"LeftShoulder"` | **Left Shoulder** | LeftShoulder | L | L1 | LB |
|
|
121
|
+
| `5` | `"RightShoulder"` | **Right Shoulder** | RightShoulder | R | R1 | RB |
|
|
122
|
+
| `6` | `"LeftTrigger"` | **Left Trigger** | LeftTrigger | L2 | ZL | LT |
|
|
123
|
+
| `7` | `"RightTrigger"` | **Right Trigger** | RightTrigger | R2 | ZR | RT |
|
|
124
|
+
| `8` | `"Back"` | **Back** | Back | Minus | Options | Back |
|
|
125
|
+
| `9` | `"Start"` | **Start** | Start | Plus | Select | Start |
|
|
126
|
+
| `10` | `"LeftStick"` | **Left Stick** | LeftStick | L3 | LeftStick | LSB |
|
|
127
|
+
| `11` | `"RightStick"` | **Right Stick** | RightStick | R3 | RightStick | RSB |
|
|
128
|
+
| `12` | `"DPadUp"` | **D-Pad Up** | DPadUp | DPadUp | DPadUp | DPadUp |
|
|
129
|
+
| `13` | `"DPadDown"` | **D-Pad Down** | DPadDown | DPadDown | DPadDown | DPadDown |
|
|
130
|
+
| `14` | `"DPadLeft"` | **D-Pad Left** | DPadLeft | DPadLeft | DPadLeft | DPadLeft |
|
|
131
|
+
| `15` | `"DPadRight"` | **D-Pad Right** | DPadRight | DPadRight | DPadRight | DPadRight |
|
|
132
|
+
|
|
133
|
+
> [!CAUTION]
|
|
134
|
+
> ***Nintendo:** Both the labels and physical positions of the A,B,X,Y buttons are different
|
|
135
|
+
> on Nintendo controllers.
|
|
136
|
+
>
|
|
137
|
+
> Set `GamepadDevice.defaultOptions.remapNintendoMode` to apply the remapping as required.
|
|
138
|
+
>
|
|
139
|
+
> - `"physical"` _(default)_ &ndash A,B,X,Y refer the physical layout of a standard controller (Left=X,Top=Y,Bottom=A,Right=B).
|
|
140
|
+
> - `"accurate"` &ndash A,B,X,Y refer to the exact Nintendo labels (Left=Y,Top=X,Bottom=B,Right=A).
|
|
141
|
+
> - `"none"` &ndash A,B,X,Y refer to the button indices 0,1,2,3 (Left=Y,Top=B,Bottom=X,Right=A).
|
|
142
|
+
>
|
|
143
|
+
> ```
|
|
144
|
+
> standard nintendo nintendo nintendo
|
|
145
|
+
> layout "physical" "accurate" "none"
|
|
146
|
+
> reference (default)
|
|
147
|
+
>
|
|
148
|
+
> Y Y X B
|
|
149
|
+
> X B X B Y A Y A
|
|
150
|
+
> A A B X
|
|
151
|
+
>
|
|
152
|
+
> 3 3 2 1
|
|
153
|
+
> 2 1 2 1 3 0 3 0
|
|
154
|
+
> 0 0 1 2
|
|
155
|
+
> ```
|
|
444
156
|
|
|
445
|
-
|
|
157
|
+
#### Assigning devices
|
|
446
158
|
|
|
447
159
|
```ts
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
// apply
|
|
452
|
-
myNode.run(myAction.easeIn());
|
|
160
|
+
InputDevice.on("deviceconnected", ({ device }) => {
|
|
161
|
+
device.meta.localPlayerId = 123
|
|
162
|
+
})
|
|
453
163
|
|
|
454
|
-
|
|
455
|
-
|
|
164
|
+
for ( const device of InputDevice.devices )
|
|
165
|
+
{
|
|
166
|
+
if ( device.meta.localPlayerId === 123 )
|
|
167
|
+
{
|
|
168
|
+
// do something
|
|
169
|
+
}
|
|
170
|
+
}
|
|
456
171
|
```
|
|
457
172
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
Beyond combining chaining actions like `sequence()`, `group()`, `repeat()` and `repeatForever()`, you can provide code that implements your own action.
|
|
173
|
+
#### Custom devices
|
|
461
174
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
Actions are stateless and reusable, so you can create complex animations once, and then run them on many nodes.
|
|
175
|
+
You can add custom devices to the device manager:
|
|
465
176
|
|
|
466
177
|
```ts
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
]),
|
|
475
|
-
Action.sequence([
|
|
476
|
-
Action.rotateByDegrees(-2, 0.33).easeOut(),
|
|
477
|
-
Action.rotateByDegrees(4, 0.34).easeInOut(),
|
|
478
|
-
Action.rotateByDegrees(-2, 0.33).easeIn(),
|
|
479
|
-
]),
|
|
480
|
-
])
|
|
481
|
-
);
|
|
482
|
-
|
|
483
|
-
// Run it over here
|
|
484
|
-
someSprite.run(rockBackAndForth);
|
|
485
|
-
|
|
486
|
-
// Run it somewhere else
|
|
487
|
-
someOtherContainer.run(rockBackAndForth);
|
|
488
|
-
```
|
|
489
|
-
|
|
490
|
-
You can combine these with dynamic actions for more variety:
|
|
178
|
+
// 1. subclass CustomDevice
|
|
179
|
+
class MySpecialDevice extends CustomDevice
|
|
180
|
+
{
|
|
181
|
+
constructor() {
|
|
182
|
+
super( "special-device" )
|
|
183
|
+
}
|
|
184
|
+
}
|
|
491
185
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
squash: (amount: number, duration: number = 0.3) => Action.sequence([
|
|
495
|
-
Action.scaleTo(amount, 1 / amount, duration / 2).easeOut(),
|
|
496
|
-
Action.scaleTo(1, duration / 2).easeIn()
|
|
497
|
-
]),
|
|
498
|
-
stretch: (amount: number, duration: number = 0.3) => Action.sequence([
|
|
499
|
-
Action.scaleTo(1 / amount, amount, duration / 2).easeOut(),
|
|
500
|
-
Action.scaleTo(1, duration / 2).easeIn()
|
|
501
|
-
]),
|
|
502
|
-
squashAndStretch: (amount: number, duration: number = 0.3) => Action.sequence([
|
|
503
|
-
MyActions.squash(amount, duration / 2),
|
|
504
|
-
MyActions.stretch(amount, duration / 2),
|
|
505
|
-
]),
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
// Small squish!
|
|
509
|
-
mySprite.run(MyActions.squashAndStretch(1.25));
|
|
510
|
-
|
|
511
|
-
// Big squish!
|
|
512
|
-
mySprite.run(MyActions.squashAndStretch(2.0));
|
|
186
|
+
// 2. add the device
|
|
187
|
+
InputDevice.add( new MySpecialDevice() )
|
|
513
188
|
```
|
|
514
189
|
|
|
515
|
-
###
|
|
190
|
+
### Navigation API
|
|
516
191
|
|
|
517
|
-
|
|
192
|
+
By default, any element with `"mousedown"` or `"pointerdown"` handlers will be navigatable.
|
|
518
193
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
const colorB = Math.sin(0.3 * t + 4) * 127 + 128;
|
|
194
|
+
Container properties | type | default | description
|
|
195
|
+
---------------------|------|---------|--------------
|
|
196
|
+
`navigationMode` | `"auto"` \| `"disabled"` \| `"target"` | `"auto"` | When set to `"auto"`, a `Container` can be navigated to if it has a `"pointerdown"` or `"mousedown"` event handler registered.
|
|
197
|
+
`isNavigatable` | `get () => boolean` | `false` | returns `true` if `navigationMode` is `"target"`, or `"auto"` and a `"pointerdown"` or `"mousedown"` event handler is registered.
|
|
198
|
+
`navigationPriority` | `number` | `0` | The priority relative to other navigation items in this group.
|
|
525
199
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
200
|
+
Navigation intent | Keyboard | Gamepads
|
|
201
|
+
------------------|------------------------|-----------------------------------
|
|
202
|
+
`"navigateLeft"` | `ArrowLeft`, `KeyA` | Left Joystick (Left), `DPadLeft`
|
|
203
|
+
`"navigateRight"` | `ArrowRight`, `KeyD` | Left Joystick (Right), `DPadRight`
|
|
204
|
+
`"navigateUp"` | `ArrowUp`, `KeyW` | Left Joystick (Up), `DPadDown`
|
|
205
|
+
`"navigateDown"` | `ArrowDown`, `KeyS` | Left Joystick (Down), `DPadUp`
|
|
206
|
+
`"navigateBack"` | `Escape`, `Backspace` | `B`, `Back`
|
|
207
|
+
`"trigger"` | `Enter,` `Space` | `A`
|
|
529
208
|
|
|
530
|
-
|
|
531
|
-
|
|
209
|
+
Container events | description
|
|
210
|
+
------------------|--------------------------------------------------------
|
|
211
|
+
`focus` | Target became focused.
|
|
212
|
+
`blur` | Target lost focus.
|
|
532
213
|
|
|
533
|
-
// Stop rainbow effect
|
|
534
|
-
mySprite.removeAction('rainbow');
|
|
535
|
-
```
|
|
536
214
|
|
|
537
|
-
>
|
|
538
|
-
>
|
|
539
|
-
> - `t` = Progress of time from 0 to 1, which has been passed through the `timingMode` function.
|
|
540
|
-
> - `dt` = delta/change in `t` since last step. Use for relative actions.
|
|
215
|
+
> [!TIP]
|
|
216
|
+
> Modify `device.options.navigation.binds` to override which keys/buttons are used for navigation.
|
|
541
217
|
>
|
|
542
|
-
>
|
|
543
|
-
|
|
544
|
-
This function will be called as many times as the renderer asks over the course of its duration.
|
|
545
|
-
|
|
546
|
-
### Custom Action (with State)
|
|
547
|
-
|
|
548
|
-
Here is a practical example:
|
|
549
|
-
|
|
550
|
-
```ts
|
|
551
|
-
// Create a custom action that relies on
|
|
552
|
-
// state (radius, inital target position).
|
|
553
|
-
const makeOrbitAction = (
|
|
554
|
-
radius: number,
|
|
555
|
-
duration: number
|
|
556
|
-
): Action => {
|
|
557
|
-
let startPos: PIXI.IPointData;
|
|
558
|
-
|
|
559
|
-
return Action.customAction(duration, (target, t, td) => {
|
|
560
|
-
if (!startPos) {
|
|
561
|
-
// Capture on first run
|
|
562
|
-
startPos = { x: target.x, y: target.y };
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
const angle = Math.PI * 2 * t;
|
|
566
|
-
|
|
567
|
-
target.position.set(
|
|
568
|
-
startPos.x + radius * Math.cos(angle),
|
|
569
|
-
startPos.y + radius * Math.sin(angle)
|
|
570
|
-
);
|
|
571
|
-
});
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
// Run the custom action
|
|
575
|
-
mySprite.run(
|
|
576
|
-
Action.repeatForever(makeOrbitAction(10, 15.0))
|
|
577
|
-
);
|
|
578
|
-
```
|
|
218
|
+
> Or set `device.options.navigation.enabled = false` to disable navigation.
|