prosthetic-hand 1.3.1 → 2.0.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 +24 -24
- package/lib/Capabilities.js +33 -0
- package/{src → lib}/Finger.js +44 -91
- package/{src → lib}/Hand.js +55 -52
- package/package.json +11 -12
- package/.babelrc +0 -4
- package/.npmignore +0 -4
- package/2016-03-20-prosthetic-hand-zooming.gif +0 -0
- package/CHANGELOG.md +0 -80
- package/DEADJOE +0 -51
- package/demos/index.html +0 -54
- package/demos/map-pan.html +0 -102
- package/demos/map-timing.html +0 -145
- package/demos/map-zoom.html +0 -131
- package/dist/api-docs.html +0 -451
- package/dist/demos/index.html +0 -54
- package/dist/demos/map-pan.html +0 -102
- package/dist/demos/map-timing.html +0 -145
- package/dist/demos/map-zoom.html +0 -131
- package/dist/prosthetic-hand.js +0 -1266
- package/dist/prosthetic-hand.js.map +0 -1
- package/gobblefile.js +0 -32
- package/prosthetic-hand-finger.gif +0 -0
- package/src/Capabilities.js +0 -92
- package/src/CustomEventPolyfill.js +0 -16
- package/src/Enums.js +0 -14
- /package/{src → lib}/IvansIndexFinger.js +0 -0
package/README.md
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
|
|
2
|
-
#
|
|
2
|
+
# 🖐️ prosthetic-hand 🖐️
|
|
3
3
|
|
|
4
|
-
A
|
|
5
|
-
unit-test touch gestures.
|
|
4
|
+
A JavaScript library to emulate mouse/touch/pointer events, designed to help unit-test touch gestures.
|
|
6
5
|
|
|
6
|
+
## Installation
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
```bash
|
|
9
|
+
npm install prosthetic-hand
|
|
10
|
+
```
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
## Usage
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
```js
|
|
15
|
+
import Hand from 'prosthetic-hand';
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
const hand = new Hand();
|
|
18
|
+
const finger = h.growFinger('pointer', { pointerType: 'touch', pressure: 0.9 });
|
|
15
19
|
|
|
20
|
+
finger
|
|
21
|
+
.wait(500)
|
|
22
|
+
.moveTo(200, 250, 0)
|
|
23
|
+
.down()
|
|
24
|
+
.moveBy(100, 150, 2000)
|
|
25
|
+
.up();
|
|
26
|
+
```
|
|
16
27
|
|
|
28
|
+
For more information see the [API documentation](http://leaflet.github.io/prosthetic-hand/api-docs.html) and [demos](http://leaflet.github.io/prosthetic-hand/demos/).
|
|
17
29
|
|
|
18
|
-
##
|
|
30
|
+
## Testing
|
|
19
31
|
|
|
20
|
-
Run `npm install
|
|
32
|
+
Run `npm install` and `npm start`, then open the URL printed in the console in your preferred browser.
|
|
21
33
|
|
|
22
|
-
|
|
34
|
+
## Building the documentation
|
|
23
35
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
Run `npm install`, then `npm start`, then point your favourite web browser at
|
|
27
|
-
`http://localhost:4567/demos/`
|
|
28
|
-
|
|
29
|
-
## Compatibility note
|
|
30
|
-
|
|
31
|
-
Please note that in order to emulate `TouchEvent`s or `PointerEvent`s, your
|
|
32
|
-
web browser must support (or polyfill) these events. Short version: use
|
|
33
|
-
Chrome when running code that emulates `TouchEvents`, and IE11 or Edge when
|
|
34
|
-
emulating `PointerEvent`s.
|
|
36
|
+
Run `npm run build-docs` and open the `api-docs.html` file generated by it.
|
|
35
37
|
|
|
36
38
|
## Contributing code
|
|
37
39
|
|
|
38
|
-
Read the Leaflet guidelines
|
|
39
|
-
|
|
40
|
-
Whenever making a bugfix or a new feature, notify [IvanSanchez](https://github.com/IvanSanchez) so that a new version can be published to NPM.
|
|
40
|
+
Read the [Leaflet guidelines](https://github.com/Leaflet/Leaflet/blob/main/CONTRIBUTING.md). Whenever making a bugfix or a new feature, notify [IvanSanchez](https://github.com/IvanSanchez) so that a new version can be published to NPM.
|
|
41
41
|
|
|
42
42
|
## Legalese
|
|
43
43
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
// Detects some browser capabilities. Only for internal use, not exposed to the
|
|
3
|
+
// API user.
|
|
4
|
+
|
|
5
|
+
// touchConstructor, for Touch
|
|
6
|
+
export var touchConstructor = true;
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
var foo = new Touch({ identifier: 0, target: document });
|
|
10
|
+
} catch(e) {
|
|
11
|
+
touchConstructor = false;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
// touchEventConstructor, for TouchEvent
|
|
16
|
+
// Weirdly, Safari on iOS has Touch constructor but no TouchEvent constructor.
|
|
17
|
+
export var touchEventConstructor = true;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
var foo = new TouchEvent('touchdown')
|
|
21
|
+
} catch(e) {
|
|
22
|
+
touchEventConstructor = false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// touch: `true` if the browser implements `TouchEvent`
|
|
26
|
+
export var touch = !!('TouchEvent' in window);
|
|
27
|
+
|
|
28
|
+
// Some bits borrowed from Leaflet's L.Browser
|
|
29
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
30
|
+
const webkit = ua.includes('webkit');
|
|
31
|
+
const chrome = ua.includes('chrome');
|
|
32
|
+
export const gecko = ua.includes('gecko') && !webkit;
|
|
33
|
+
export const safari = !chrome && ua.includes('safari');
|
package/{src → lib}/Finger.js
RENAMED
|
@@ -8,12 +8,12 @@ import * as capabilities from './Capabilities.js';
|
|
|
8
8
|
// ID whenever they go down.
|
|
9
9
|
var fingerIdSequence = 1;
|
|
10
10
|
|
|
11
|
-
//
|
|
11
|
+
// 🖐️class Finger
|
|
12
12
|
// Represents a finger, capable of performing single touch/pointer/mouse synthetic
|
|
13
13
|
// events.
|
|
14
14
|
|
|
15
15
|
/*
|
|
16
|
-
|
|
16
|
+
🖐️example
|
|
17
17
|
|
|
18
18
|
```js
|
|
19
19
|
var h = new Hand();
|
|
@@ -26,7 +26,7 @@ fatFinger.wait(500).moveTo(200, 250, 0).down().moveBy(100, 150, 2000).up();
|
|
|
26
26
|
*/
|
|
27
27
|
export default class Finger {
|
|
28
28
|
|
|
29
|
-
//
|
|
29
|
+
// 🖐️factory Finger(eventMode: String, options?: Finger state): Finger
|
|
30
30
|
// Instantiates a new `Finger`. `eventMode` must be `mouse`, `touch` or `pointer`
|
|
31
31
|
// for `MouseEvent`s, `TouchEvent`s or `PointerEvent`s, respectively.
|
|
32
32
|
constructor(eventMode, options) {
|
|
@@ -40,36 +40,36 @@ export default class Finger {
|
|
|
40
40
|
/// TODO: parkinsonFactor or shakesFactor or jitteryness or something
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
//
|
|
43
|
+
// 🖐️section Finger state
|
|
44
44
|
// The internal state of a `Finger` has options which will be reflected as
|
|
45
45
|
// properties of the events fired afterwards. Some of these state options
|
|
46
46
|
// apply only to a specific event mode.
|
|
47
47
|
this._state = Object.assign({}, {
|
|
48
|
-
//
|
|
48
|
+
// 🖐️option x: Number; The number of pixels from the left boundary the finger is at.
|
|
49
49
|
x: 0,
|
|
50
50
|
|
|
51
|
-
//
|
|
51
|
+
// 🖐️option y: Number; The number of pixels from the top boundary the finger is at.
|
|
52
52
|
y: 0,
|
|
53
53
|
|
|
54
|
-
//
|
|
54
|
+
// 🖐️option down: Boolean; Whether the finger is down (clicking/touching/pressing) or not. This is referred to as "active" in some of the events specifications.
|
|
55
55
|
down: false,
|
|
56
56
|
|
|
57
|
-
//
|
|
57
|
+
// 🖐️option pressure: Number = 0.5; The value for [`Touch.force`](`https://developer.mozilla.org/docs/Web/API/Touch/force`) or [`PointerEvent.pressure`](https://developer.mozilla.org/docs/Web/API/PointerEvent/pressure), between `0.0` and `1.0`
|
|
58
58
|
pressure: 0.5,
|
|
59
59
|
|
|
60
|
-
//
|
|
60
|
+
// 🖐️option tiltX: Number = 0; The value for [`PointerEvent.tiltX`](https://developer.mozilla.org/docs/Web/API/PointerEvent/tiltX)
|
|
61
61
|
tiltX: 0,
|
|
62
62
|
|
|
63
|
-
//
|
|
63
|
+
// 🖐️option tiltY: Number = 0; The value for [`PointerEvent.tiltX`](https://developer.mozilla.org/docs/Web/API/PointerEvent/tiltY)
|
|
64
64
|
tiltY: 0,
|
|
65
65
|
|
|
66
|
-
//
|
|
66
|
+
// 🖐️option width: Number = 25; The value for [`Touch.radiusX`](`https://developer.mozilla.org/docs/Web/API/Touch/radiusX`) or [`PointerEvent.width`](https://developer.mozilla.org/docs/Web/API/PointerEvent/width)
|
|
67
67
|
width: 25,
|
|
68
68
|
|
|
69
|
-
//
|
|
69
|
+
// 🖐️option radiusY: Number = 25; The value for [`Touch.radiusY`](`https://developer.mozilla.org/docs/Web/API/Touch/radiusY`) or [`PointerEvent.height`](https://developer.mozilla.org/docs/Web/API/PointerEvent/height)
|
|
70
70
|
height: 25,
|
|
71
71
|
|
|
72
|
-
//
|
|
72
|
+
// 🖐️option pointerType: String = 'pen'; The value for [`PointerEvent.pointerType`](https://developer.mozilla.org/docs/Web/API/PointerEvent/pointerType)
|
|
73
73
|
pointerType: 'pen'
|
|
74
74
|
}, options);
|
|
75
75
|
|
|
@@ -111,15 +111,9 @@ export default class Finger {
|
|
|
111
111
|
} else {
|
|
112
112
|
this._initGraphicCircle();
|
|
113
113
|
}
|
|
114
|
-
if (!capabilities.pointer) {
|
|
115
|
-
console.warn('This browser cannot emulate pointer events.');
|
|
116
|
-
}
|
|
117
114
|
} else {
|
|
118
115
|
this._mode = 'mouse';
|
|
119
116
|
this._initGraphicCircle();
|
|
120
|
-
if (!capabilities.mouse) {
|
|
121
|
-
console.warn('This browser cannot emulate mouse events.');
|
|
122
|
-
}
|
|
123
117
|
}
|
|
124
118
|
|
|
125
119
|
|
|
@@ -135,28 +129,28 @@ export default class Finger {
|
|
|
135
129
|
}
|
|
136
130
|
|
|
137
131
|
|
|
138
|
-
//
|
|
132
|
+
// 🖐️method isIdle(): Boolean
|
|
139
133
|
// Returns true when the finger has no more pending movements/waits/wiggles/etc.
|
|
140
134
|
isIdle() {
|
|
141
135
|
return !(this._movements.length);
|
|
142
136
|
}
|
|
143
137
|
|
|
144
138
|
|
|
145
|
-
//
|
|
139
|
+
// 🖐️method down(delay?: Number): this
|
|
146
140
|
// Puts the finger down, optionally after a delay.
|
|
147
141
|
down(delay) {
|
|
148
142
|
return this.update({ down: true, getState: this._falseFn, duration: delay || 0 });
|
|
149
143
|
}
|
|
150
144
|
|
|
151
145
|
|
|
152
|
-
//
|
|
146
|
+
// 🖐️method up(options?: {}): this
|
|
153
147
|
// Lifts the finger up, after an optional delay.
|
|
154
148
|
up(delay) {
|
|
155
149
|
return this.update({ down: false, getState: this._falseFn, duration: delay || 0 });
|
|
156
150
|
}
|
|
157
151
|
|
|
158
152
|
|
|
159
|
-
//
|
|
153
|
+
// 🖐️method wait(delay): this
|
|
160
154
|
// Don't move this finger for `delay` milliseconds.
|
|
161
155
|
wait(delay) {
|
|
162
156
|
this._queueMove({finalState: this._finalState, getState: this._falseFn, duration: delay});
|
|
@@ -164,7 +158,7 @@ export default class Finger {
|
|
|
164
158
|
}
|
|
165
159
|
|
|
166
160
|
|
|
167
|
-
//
|
|
161
|
+
// 🖐️method waitUntil(timestamp): this
|
|
168
162
|
// Don't move this finger until the given timestamp is reached.
|
|
169
163
|
waitUntil(timestamp) {
|
|
170
164
|
if (this._movements.length) {
|
|
@@ -185,7 +179,7 @@ export default class Finger {
|
|
|
185
179
|
}
|
|
186
180
|
|
|
187
181
|
|
|
188
|
-
//
|
|
182
|
+
// 🖐️method update(options?: {}): this
|
|
189
183
|
// Updates some of the finger options, like pressure or touch angle,
|
|
190
184
|
// without disturbing its movement, after an optional delay.
|
|
191
185
|
update(options, delay) {
|
|
@@ -194,7 +188,7 @@ export default class Finger {
|
|
|
194
188
|
}
|
|
195
189
|
|
|
196
190
|
|
|
197
|
-
//
|
|
191
|
+
// 🖐️method reset(options?: {}): this
|
|
198
192
|
// Clears all the queued movements for this finger and immediately lifts it up
|
|
199
193
|
reset(options) {
|
|
200
194
|
return this;
|
|
@@ -202,7 +196,7 @@ export default class Finger {
|
|
|
202
196
|
|
|
203
197
|
|
|
204
198
|
|
|
205
|
-
//
|
|
199
|
+
// 🖐️method moveTo(x: Number, y: Number, delay: Number, options?: {}): this
|
|
206
200
|
// Queues moving this finger to an absolute position at `(x, y)`; the
|
|
207
201
|
// movement will last for `delay` milliseconds.
|
|
208
202
|
moveTo(x, y, delay) {
|
|
@@ -210,7 +204,7 @@ export default class Finger {
|
|
|
210
204
|
}
|
|
211
205
|
|
|
212
206
|
|
|
213
|
-
//
|
|
207
|
+
// 🖐️method moveBy(x: Number, y: Number, delay: Number, options?: {}): this
|
|
214
208
|
// Queues a move of this finger to an position relative to its last position
|
|
215
209
|
// plus`(x, y)`; the movement will last for `delay` milliseconds.
|
|
216
210
|
moveBy(x, y, delay) {
|
|
@@ -270,7 +264,7 @@ export default class Finger {
|
|
|
270
264
|
|
|
271
265
|
|
|
272
266
|
// Returns the timestamp when the next movement will be finished
|
|
273
|
-
//
|
|
267
|
+
// 🖐️method getNextMoveEndTime(): Number|undefined
|
|
274
268
|
getNextMoveEndTime() {
|
|
275
269
|
if (!this._movements.length) {
|
|
276
270
|
return undefined;
|
|
@@ -280,7 +274,7 @@ export default class Finger {
|
|
|
280
274
|
|
|
281
275
|
|
|
282
276
|
/*
|
|
283
|
-
*
|
|
277
|
+
* 🖐️method getEvents(timestamp?: Number, justOne: Boolean): []
|
|
284
278
|
* Updates the private properties of the finger (x, y, timestamp) by
|
|
285
279
|
* running the next movement(s) as far as indicated by the timestamp (or
|
|
286
280
|
* as fas as to `performance.now()`), then checks if the state has changed
|
|
@@ -403,7 +397,7 @@ export default class Finger {
|
|
|
403
397
|
|
|
404
398
|
|
|
405
399
|
|
|
406
|
-
//
|
|
400
|
+
// 🖐️method private_asTouch(): Touch
|
|
407
401
|
// Returns an instance of `Touch` representing the current state of the finger
|
|
408
402
|
// Note this is not an event - a `TouchEvent` must be created later, with several
|
|
409
403
|
// `Touch`es.
|
|
@@ -426,23 +420,7 @@ export default class Finger {
|
|
|
426
420
|
});
|
|
427
421
|
} else {
|
|
428
422
|
|
|
429
|
-
if (capabilities.
|
|
430
|
-
touch = document.createTouch(
|
|
431
|
-
window, // view
|
|
432
|
-
this._touchTargetWhenDowned, // target
|
|
433
|
-
this._id, // identifier
|
|
434
|
-
this._state.x, // clientX
|
|
435
|
-
this._state.y, // clientY
|
|
436
|
-
this._state.x, // screenX
|
|
437
|
-
this._state.y, // screenY
|
|
438
|
-
|
|
439
|
-
// Inconsistency: These break anything other than chrome:
|
|
440
|
-
25, // radiusX
|
|
441
|
-
25, // radiusY
|
|
442
|
-
0, // rotationAngle
|
|
443
|
-
this._state.pressure // force
|
|
444
|
-
);
|
|
445
|
-
} else if (capabilities.gecko) {
|
|
423
|
+
if (capabilities.gecko) {
|
|
446
424
|
touch = document.createTouch(
|
|
447
425
|
window, // view
|
|
448
426
|
this._touchTargetWhenDowned, // target
|
|
@@ -472,21 +450,21 @@ export default class Finger {
|
|
|
472
450
|
return touch;
|
|
473
451
|
}
|
|
474
452
|
|
|
475
|
-
//
|
|
453
|
+
// 🖐️method private_asPointerEvent(): PointerEvent
|
|
476
454
|
// Returns an instance of `PointerEvent` representing the current state of the finger
|
|
477
455
|
_asPointerEvent(evType) {
|
|
478
|
-
|
|
456
|
+
return new PointerEvent('pointer' + evType, {
|
|
479
457
|
bubbles: true,
|
|
480
458
|
button: 0, // Moz doesn't use -1 when no buttons are pressed, WTF?
|
|
481
459
|
// buttons: this._state.down ? 1 : 0,
|
|
482
|
-
//
|
|
460
|
+
// detail: (evType === 'down' || evType === 'up') ? 1 : 0, // TODO: count consecutive clicks
|
|
483
461
|
clientX: this._state.x,
|
|
484
462
|
clientY: this._state.y,
|
|
485
463
|
screenX: this._state.x, /// TODO: Handle page scrolling
|
|
486
464
|
screenY: this._state.y,
|
|
487
465
|
pageX: this._state.x,
|
|
488
466
|
pageY: this._state.y,
|
|
489
|
-
pointerType:
|
|
467
|
+
pointerType: this._state.pointerType,
|
|
490
468
|
pointerId: this._id,
|
|
491
469
|
isPrimary: this._id === 1,
|
|
492
470
|
width: this._state.width,
|
|
@@ -496,49 +474,24 @@ export default class Finger {
|
|
|
496
474
|
pressure: this._state.pressure
|
|
497
475
|
// target: document.elementFromPoint(this._state.x, this._state.y), // works with viewport coords
|
|
498
476
|
});
|
|
499
|
-
return ev;
|
|
500
477
|
}
|
|
501
478
|
|
|
502
|
-
//
|
|
479
|
+
// 🖐️method private_asMouseEvent(evType: String): PointerEvent
|
|
503
480
|
// Returns an instance of `PointerEvent` representing the current state of the finger
|
|
504
481
|
_asMouseEvent(evType) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
// target: document.elementFromPoint(this._state.x, this._state.y), // works with viewport coords
|
|
519
|
-
});
|
|
520
|
-
} else {
|
|
521
|
-
// For legacy browsers and PhantomJS
|
|
522
|
-
ev = document.createEvent('MouseEvent');
|
|
523
|
-
ev.initMouseEvent(
|
|
524
|
-
'mouse' + evType, // Type
|
|
525
|
-
true, // canBubble
|
|
526
|
-
true, // cancellable
|
|
527
|
-
window, // view
|
|
528
|
-
0, // detail
|
|
529
|
-
this._state.x, // screenX
|
|
530
|
-
this._state.y, // screenY
|
|
531
|
-
this._state.x, // clientX
|
|
532
|
-
this._state.y, // clientY
|
|
533
|
-
false, // ctrlKey
|
|
534
|
-
false, // altKey
|
|
535
|
-
false, // shiftKey
|
|
536
|
-
false, // metaKey
|
|
537
|
-
0, // button
|
|
538
|
-
null // relatedTarget
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
return ev;
|
|
482
|
+
return new MouseEvent('mouse' + evType, {
|
|
483
|
+
bubbles: true,
|
|
484
|
+
button: 0, // Moz doesn't use -1 when no buttons are pressed, WTF?
|
|
485
|
+
buttons: this._state.down ? 1 : 0,
|
|
486
|
+
detail: (evType === 'down' || evType === 'up') ? 1 : 0, // TODO: count consecutive clicks
|
|
487
|
+
clientX: this._state.x,
|
|
488
|
+
clientY: this._state.y,
|
|
489
|
+
screenX: this._state.x, /// TODO: Handle page scrolling
|
|
490
|
+
screenY: this._state.y,
|
|
491
|
+
pageX: this._state.x,
|
|
492
|
+
pageY: this._state.y,
|
|
493
|
+
// target: document.elementFromPoint(this._state.x, this._state.y), // works with viewport coords
|
|
494
|
+
});
|
|
542
495
|
}
|
|
543
496
|
|
|
544
497
|
|
package/{src → lib}/Hand.js
RENAMED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
|
|
2
2
|
import Finger from './Finger.js';
|
|
3
|
-
import * as enums from './Enums.js';
|
|
4
3
|
import * as capabilities from './Capabilities.js';
|
|
5
|
-
import {} from './CustomEventPolyfill.js';
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
const TimingMode = {
|
|
6
|
+
Interval: 'INTERVAL',
|
|
7
|
+
Minimal: 'MINIMAL',
|
|
8
|
+
Instant: 'INSTANT',
|
|
9
|
+
Frame: 'FRAME',
|
|
10
|
+
FastFrame: 'FAST_FRAME'
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// 🖐️class Hand
|
|
8
14
|
// Represents a set of `Finger`s, capable of performing synthetic touch gestures
|
|
9
15
|
|
|
10
16
|
/*
|
|
11
|
-
|
|
17
|
+
🖐️example
|
|
12
18
|
|
|
13
19
|
```js
|
|
14
20
|
var h = new Hand({ timing: '20ms' });
|
|
@@ -17,7 +23,7 @@ var h = new Hand({ timing: '20ms' });
|
|
|
17
23
|
*/
|
|
18
24
|
export default class Hand {
|
|
19
25
|
|
|
20
|
-
//
|
|
26
|
+
// 🖐️factory Hand(options?: Hand options): Hand
|
|
21
27
|
// Instantiates a new `Hand` with the given options.
|
|
22
28
|
constructor(options) {
|
|
23
29
|
|
|
@@ -32,71 +38,71 @@ export default class Hand {
|
|
|
32
38
|
|
|
33
39
|
/// TODO: Timing modes: minimal, interval, frames
|
|
34
40
|
|
|
35
|
-
//
|
|
41
|
+
// 🖐️option timing: Timing= '20ms'
|
|
36
42
|
// Defines how often new events will be fired, in one of the possible
|
|
37
43
|
// timing modes
|
|
38
44
|
|
|
39
|
-
//
|
|
45
|
+
// 🖐️miniclass Timing (Hand)
|
|
40
46
|
this._timeInterval = 20;
|
|
41
|
-
this._timingMode =
|
|
47
|
+
this._timingMode = TimingMode.Interval;
|
|
42
48
|
this._framesPending = 0;
|
|
43
49
|
if (options.timing) {
|
|
44
50
|
var timing = options.timing.toString();
|
|
45
51
|
|
|
46
|
-
//
|
|
52
|
+
// 🖐️option ms
|
|
47
53
|
// Preceded by a number (e.g. `'20ms'`), this mode triggers an event
|
|
48
54
|
// dispatch every that many milliseconds.
|
|
49
55
|
if (timing.match(/^\d+ms$/)) {
|
|
50
|
-
this._timingMode =
|
|
56
|
+
this._timingMode = TimingMode.Interval;;
|
|
51
57
|
this._timeInterval = parseInt(timing);
|
|
52
58
|
}
|
|
53
59
|
|
|
54
|
-
//
|
|
60
|
+
// 🖐️option frame
|
|
55
61
|
// This mode dispatches an event every [animation frame](https://developer.mozilla.org/docs/Web/API/Window/requestAnimationFrame).
|
|
56
62
|
if (timing === 'frame') {
|
|
57
|
-
this._timingMode =
|
|
63
|
+
this._timingMode = TimingMode.Frame;
|
|
58
64
|
this._timeInterval = parseInt(timing);
|
|
59
65
|
}
|
|
60
66
|
|
|
61
|
-
//
|
|
67
|
+
// 🖐️option minimal
|
|
62
68
|
// This mode triggers an event dispatch per finger change, and ensures
|
|
63
69
|
// that every move can trigger its own event (no two movements will be
|
|
64
70
|
// rolled into one event if they are very close).
|
|
65
71
|
if (timing === 'minimal') {
|
|
66
|
-
this._timingMode =
|
|
72
|
+
this._timingMode = TimingMode.Minimal;
|
|
67
73
|
this._timeInterval = false;
|
|
68
74
|
}
|
|
69
75
|
|
|
70
|
-
//
|
|
76
|
+
// 🖐️option instant
|
|
71
77
|
// Like the `minimal` mode, but ignores timings completely and dispatches
|
|
72
78
|
// all events instantaneously. This might cause misbehaviour in graphical
|
|
73
79
|
// browsers, and the `onStart` and `onStop` callbacks will be called at.
|
|
74
80
|
// every step of the movement (as the movement ends before the next step
|
|
75
81
|
// is chained in)
|
|
76
82
|
if (timing === 'instant') {
|
|
77
|
-
this._timingMode =
|
|
83
|
+
this._timingMode = TimingMode.Instant;
|
|
78
84
|
this._timeInterval = false;
|
|
79
85
|
}
|
|
80
86
|
|
|
81
|
-
//
|
|
87
|
+
// 🖐️option fastframe
|
|
82
88
|
// This mode ignores timings completely like the `instant` mode, and
|
|
83
89
|
// dispatches a new event every so many frames.
|
|
84
90
|
if (timing === 'fastframe') {
|
|
85
|
-
this._timingMode =
|
|
91
|
+
this._timingMode = TimingMode.FastFrame;
|
|
86
92
|
this._timeInterval = parseInt(timing);
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
95
|
|
|
90
|
-
//
|
|
96
|
+
// 🖐️class Hand
|
|
91
97
|
|
|
92
|
-
//
|
|
98
|
+
// 🖐️option onStart: Function
|
|
93
99
|
// If set to a callback function, it will be called (with the `Hand`
|
|
94
100
|
// as its only argument) whenever the movements start.
|
|
95
101
|
if (options.onStart) {
|
|
96
102
|
this._onStart = options.onStart;
|
|
97
103
|
}
|
|
98
104
|
|
|
99
|
-
//
|
|
105
|
+
// 🖐️option onStop: Function
|
|
100
106
|
// If set to a callback function, it will be called (with the `Hand`
|
|
101
107
|
// as its only argument) whenever the movements are completed.
|
|
102
108
|
if (options.onStop) {
|
|
@@ -111,7 +117,7 @@ export default class Hand {
|
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
|
|
114
|
-
//
|
|
120
|
+
// 🖐️method growFinger(eventMode, options): Finger
|
|
115
121
|
// Creates a new `Finger` with the same parameters as the [`Finger` constructor](#finger-finger),
|
|
116
122
|
// and adds it to the hand.
|
|
117
123
|
growFinger(fingerMode, options) {
|
|
@@ -130,7 +136,7 @@ export default class Hand {
|
|
|
130
136
|
|
|
131
137
|
|
|
132
138
|
|
|
133
|
-
//
|
|
139
|
+
// 🖐️method fingerIsBusy(): this
|
|
134
140
|
// Used by this hand's fingers to signal that there are movements to be
|
|
135
141
|
// performed by at least one finger.
|
|
136
142
|
fingerIsBusy() {
|
|
@@ -138,10 +144,10 @@ export default class Hand {
|
|
|
138
144
|
/// TODO: Start up the event loop
|
|
139
145
|
|
|
140
146
|
if (this._fingersAreIdle) {
|
|
141
|
-
//
|
|
147
|
+
// 🖐️section
|
|
142
148
|
// Use `document.addEventListener('prostheticHandStop', fn)` to
|
|
143
149
|
// do stuff with it.
|
|
144
|
-
//
|
|
150
|
+
// 🖐️event prostheticHandStart: CustomEvent
|
|
145
151
|
// Fired when all movements are complete.
|
|
146
152
|
document.dispatchEvent(new CustomEvent('prostheticHandStart', {target: this}));
|
|
147
153
|
|
|
@@ -156,32 +162,18 @@ export default class Hand {
|
|
|
156
162
|
return this;
|
|
157
163
|
}
|
|
158
164
|
|
|
159
|
-
//
|
|
165
|
+
// 🖐️method fingerIsIdle(): this
|
|
160
166
|
// Used by this hand's fingers to signal that one finger has finished doing
|
|
161
167
|
// all the queued movements.
|
|
162
168
|
fingerIsIdle() {
|
|
163
169
|
|
|
164
170
|
if (this._fingers.every( f => f.isIdle())) {
|
|
165
|
-
|
|
166
|
-
if (!this._fingersAreIdle) {
|
|
167
|
-
// 🖑event prostheticHandStop: CustomEvent
|
|
168
|
-
// Fired when all movements are complete.
|
|
169
|
-
|
|
170
|
-
document.dispatchEvent(new CustomEvent('prostheticHandStop', {target: this}));
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (this._onStop && this._onStop instanceof Function) {
|
|
174
|
-
this._onStop(this);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
171
|
this._fingersAreIdle = true;
|
|
180
172
|
}
|
|
181
173
|
}
|
|
182
174
|
|
|
183
175
|
|
|
184
|
-
//
|
|
176
|
+
// 🖐️method sync(delay): this
|
|
185
177
|
// Synchronizes the finger movements by adding a delay of **at least** `delay`
|
|
186
178
|
// milliseconds to each finger. After a sync, the movements of the fingers
|
|
187
179
|
// will happen at exactly the same time.
|
|
@@ -207,13 +199,13 @@ export default class Hand {
|
|
|
207
199
|
|
|
208
200
|
|
|
209
201
|
|
|
210
|
-
//
|
|
202
|
+
// 🖐️method private_dispatchEvents(): this
|
|
211
203
|
// Updates all the fingers, fetching their events/touchpoints, and dispatches
|
|
212
204
|
// all `Event`s triggered by the update.
|
|
213
205
|
// This is meant to be called on an internal timer.
|
|
214
206
|
_dispatchEvents(timestamp) {
|
|
215
207
|
|
|
216
|
-
//
|
|
208
|
+
// 🖐️event prostheticHandTick: CustomEvent
|
|
217
209
|
// Fired a movement is about to start, just before the mouse/touch/pointer
|
|
218
210
|
// events are fired.
|
|
219
211
|
document.dispatchEvent(new CustomEvent('prostheticHandStart', {target: this}));
|
|
@@ -229,9 +221,9 @@ export default class Hand {
|
|
|
229
221
|
var hasTouchEnd = false;
|
|
230
222
|
var touchEndTarget = undefined;
|
|
231
223
|
|
|
232
|
-
var fast = this._timingMode ===
|
|
233
|
-
this._timingMode ===
|
|
234
|
-
this._timingMode ===
|
|
224
|
+
var fast = this._timingMode === TimingMode.Minimal ||
|
|
225
|
+
this._timingMode === TimingMode.Instant ||
|
|
226
|
+
this._timingMode === TimingMode.FastFrame;
|
|
235
227
|
|
|
236
228
|
this._fingers.forEach(f=> {
|
|
237
229
|
|
|
@@ -453,7 +445,18 @@ export default class Hand {
|
|
|
453
445
|
|
|
454
446
|
|
|
455
447
|
_scheduleNextDispatch(){
|
|
456
|
-
if (
|
|
448
|
+
if (this._fingersAreIdle) {
|
|
449
|
+
// 🖐️event prostheticHandStop: CustomEvent
|
|
450
|
+
// Fired when all movements are complete.
|
|
451
|
+
|
|
452
|
+
document.dispatchEvent(new CustomEvent('prostheticHandStop', {target: this}));
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
if (this._onStop && this._onStop instanceof Function) {
|
|
456
|
+
this._onStop(this);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
} else {
|
|
457
460
|
|
|
458
461
|
// Calculate time for next movement end. Could be refactored out for
|
|
459
462
|
// some timing modes.
|
|
@@ -469,19 +472,19 @@ export default class Hand {
|
|
|
469
472
|
});
|
|
470
473
|
|
|
471
474
|
|
|
472
|
-
if (this._timingMode ===
|
|
475
|
+
if (this._timingMode === TimingMode.Interval) {
|
|
473
476
|
this._nextDispatch = setTimeout(this._dispatchEvents.bind(this), this._timeInterval);
|
|
474
477
|
|
|
475
|
-
} else if (this._timingMode ===
|
|
478
|
+
} else if (this._timingMode === TimingMode.Minimal) {
|
|
476
479
|
this._nextDispatch = setTimeout(this._dispatchEvents.bind(this), min - performance.now());
|
|
477
480
|
|
|
478
|
-
} else if (this._timingMode ===
|
|
481
|
+
} else if (this._timingMode === TimingMode.Instant) {
|
|
479
482
|
return this._dispatchEvents(min);
|
|
480
483
|
|
|
481
|
-
} else if (this._timingMode ===
|
|
484
|
+
} else if (this._timingMode === TimingMode.Frame) {
|
|
482
485
|
this._nextDispatch = requestAnimationFrame( this._dispatchEvents.bind(this) );
|
|
483
486
|
|
|
484
|
-
} else if (this._timingMode ===
|
|
487
|
+
} else if (this._timingMode === TimingMode.FastFrame) {
|
|
485
488
|
this._nextDispatch = requestAnimationFrame( function() {
|
|
486
489
|
this._dispatchEvents(min);
|
|
487
490
|
}.bind(this));
|