Pārlūkot izejas kodu

Refine the PointerTracker

customisations
alemart 10 mēnešus atpakaļ
vecāks
revīzija
144f4b06a1

+ 1
- 1
docs/api/trackable-pointer.md Parādīt failu

@@ -26,7 +26,7 @@ The phase of the pointer. It's one of the following strings:
26 26
 * `"stationary"`: the user did not move the pointer in this frame
27 27
 * `"moved"`: the user moved the pointer in this frame
28 28
 * `"ended"`: the tracking ended in this frame (e.g., a finger has just been lifted from the screen)
29
-* `"canceled"`: the tracking was canceled in this frame (e.g., the screen orientation of the device has just been changed)
29
+* `"canceled"`: the tracking was canceled in this frame (e.g., the page has just lost focus)
30 30
 
31 31
 ### position
32 32
 

+ 14
- 0
src/sources/pointer-source.ts Parādīt failu

@@ -48,6 +48,7 @@ export class PointerSource implements Source
48 48
         this._queue = [];
49 49
         this._viewport = null;
50 50
         this._onPointerEvent = this._onPointerEvent.bind(this);
51
+        this._cancelEvent = this._cancelEvent.bind(this);
51 52
     }
52 53
 
53 54
     /**
@@ -143,6 +144,17 @@ export class PointerSource implements Source
143 144
     private _onPointerEvent(event: PointerEvent): void
144 145
     {
145 146
         this._queue.push(event);
147
+        event.preventDefault();
148
+    }
149
+
150
+    /**
151
+     * Cancel event
152
+     * @param event
153
+     */
154
+    private _cancelEvent(event: Event): void
155
+    {
156
+        if(event.cancelable)
157
+            event.preventDefault();
146 158
     }
147 159
 
148 160
     /**
@@ -157,6 +169,7 @@ export class PointerSource implements Source
157 169
         canvas.addEventListener('pointercancel', this._onPointerEvent);
158 170
         canvas.addEventListener('pointerleave', this._onPointerEvent);
159 171
         canvas.addEventListener('pointerenter', this._onPointerEvent);
172
+        canvas.addEventListener('touchstart', this._cancelEvent, { passive: false });
160 173
     }
161 174
 
162 175
     /**
@@ -165,6 +178,7 @@ export class PointerSource implements Source
165 178
      */
166 179
     private _removeEventListeners(canvas: HTMLCanvasElement): void
167 180
     {
181
+        canvas.removeEventListener('touchstart', this._cancelEvent);
168 182
         canvas.removeEventListener('pointerenter', this._onPointerEvent);
169 183
         canvas.removeEventListener('pointerleave', this._onPointerEvent);
170 184
         canvas.removeEventListener('pointercancel', this._onPointerEvent);

+ 100
- 6
src/trackers/pointer-tracker/pointer-tracker.ts Parādīt failu

@@ -88,6 +88,9 @@ export class PointerTracker implements Tracker
88 88
     /** time of the previous update */
89 89
     private _previousUpdateTime: DOMHighResTimeStamp;
90 90
 
91
+    /** helper flag */
92
+    private _wantToReset: boolean;
93
+
91 94
 
92 95
 
93 96
     /**
@@ -101,6 +104,8 @@ export class PointerTracker implements Tracker
101 104
         this._newPointers = new Map();
102 105
         this._previousOutput = this._generateOutput();
103 106
         this._previousUpdateTime = Number.POSITIVE_INFINITY;
107
+        this._wantToReset = false;
108
+        this._resetInTheNextUpdate = this._resetInTheNextUpdate.bind(this);
104 109
     }
105 110
 
106 111
     /**
@@ -138,6 +143,9 @@ export class PointerTracker implements Tracker
138 143
         // link the pointer source to the viewport
139 144
         this._source._setViewport(this._viewport);
140 145
 
146
+        // reset trackables
147
+        document.addEventListener('visibilitychange', this._resetInTheNextUpdate);
148
+
141 149
         // done!
142 150
         return Speedy.Promise.resolve();
143 151
     }
@@ -154,6 +162,8 @@ export class PointerTracker implements Tracker
154 162
         this._activePointers.clear();
155 163
         this._newPointers.clear();
156 164
 
165
+        document.removeEventListener('visibilitychange', this._resetInTheNextUpdate);
166
+
157 167
         return Speedy.Promise.resolve();
158 168
     }
159 169
 
@@ -172,17 +182,23 @@ export class PointerTracker implements Tracker
172 182
         const inverseDeltaTime = (deltaTime > 1e-5) ? 1 / deltaTime : 60; // 1/dt = 1 / (1/60) with 60 fps
173 183
 
174 184
         // remove inactive trackables from the previous frame (update cycle)
175
-        const inactiveTrackables = this._findUnwantedTrackables();
185
+        const inactiveTrackables = this._findInactiveTrackables();
176 186
         for(let i = inactiveTrackables.length - 1; i >= 0; i--)
177 187
             this._activePointers.delete(inactiveTrackables[i].id);
178 188
 
179 189
         // make all active trackables stationary
180
-        this._activePointers.forEach((trackable, id) => {
181
-            this._activePointers.set(id, Object.assign({}, trackable, {
182
-                phase: 'stationary'
183
-            }));
190
+        this._updateAllTrackables({
191
+            phase: 'stationary',
192
+            velocity: Vector2.Zero(),
193
+            deltaPosition: Vector2.Zero()
184 194
         });
185 195
 
196
+        // want to reset?
197
+        if(this._wantToReset) {
198
+            this._reset();
199
+            this._wantToReset = false;
200
+        }
201
+
186 202
         // consume events
187 203
         let event: Nullable<PointerEvent>;
188 204
         while((event = this._source!._consume()) !== null) {
@@ -238,6 +254,10 @@ export class PointerTracker implements Tracker
238 254
                 }
239 255
             }
240 256
 
257
+            // discard previously canceled pointers (e.g., with a visibilitychange event)
258
+            if(previous?.phase == 'canceled')
259
+                continue;
260
+
241 261
             // more special rules
242 262
             switch(event.type) {
243 263
                 case 'pointermove':
@@ -249,6 +269,11 @@ export class PointerTracker implements Tracker
249 269
                     if(event.buttons == 0 || previous?.phase == 'began' || current?.phase == 'began')
250 270
                         continue;
251 271
                     break;
272
+
273
+                case 'pointercancel': // purge everything
274
+                    this._reset();
275
+                    this._newPointers.clear();
276
+                    continue;
252 277
             }
253 278
 
254 279
             // determine the current position
@@ -287,6 +312,7 @@ export class PointerTracker implements Tracker
287 312
         // update trackables
288 313
         this._newPointers.forEach((trackable, id) => this._activePointers.set(id, trackable));
289 314
         this._newPointers.clear();
315
+        this._advanceAllStationaryTrackables(deltaTime);
290 316
 
291 317
         // generate output
292 318
         this._previousOutput = this._generateOutput();
@@ -337,6 +363,60 @@ export class PointerTracker implements Tracker
337 363
     }
338 364
 
339 365
     /**
366
+     * Update all active pointers
367
+     * @param fields
368
+     */
369
+    private _updateAllTrackables(fields: Partial<TrackablePointer>): void
370
+    {
371
+        this._activePointers.forEach((trackable, id) => {
372
+            this._activePointers.set(id, Object.assign({}, trackable, fields));
373
+        });
374
+    }
375
+
376
+    /**
377
+     * Advance the elapsed time of all stationary pointers
378
+     * @param deltaTime
379
+     */
380
+    private _advanceAllStationaryTrackables(deltaTime: number): void
381
+    {
382
+        this._activePointers.forEach((trackable, id) => {
383
+            if(trackable.phase == 'stationary') {
384
+                (trackable as any).elapsedTime += deltaTime;
385
+                /*
386
+                this._activePointers.set(id, Object.assign({}, trackable, {
387
+                    elapsedTime: trackable.elapsedTime + deltaTime
388
+                }));
389
+                */
390
+            }
391
+        });
392
+    }
393
+
394
+    /**
395
+     * Cancel all active pointers and consume all events
396
+     * @param deltaTime
397
+     */
398
+    private _reset(): void
399
+    {
400
+        // cancel all active pointers
401
+        this._updateAllTrackables({
402
+            phase: 'canceled',
403
+            deltaPosition: Vector2.Zero(),
404
+            velocity: Vector2.Zero(),
405
+        });
406
+
407
+        // consume all events
408
+        while(this._source!._consume() !== null);
409
+    }
410
+
411
+    /**
412
+     * Reset in the next update of the tracker
413
+     */
414
+    private _resetInTheNextUpdate(): void
415
+    {
416
+        this._wantToReset = true;
417
+    }
418
+
419
+    /**
340 420
      * As a convenience, let's make sure that a primary pointer, if any exists,
341 421
      * is at the beginning of the trackables array
342 422
      * @param trackables
@@ -344,6 +424,20 @@ export class PointerTracker implements Tracker
344 424
      */
345 425
     private _sortTrackables(trackables: TrackablePointer[]): TrackablePointer[]
346 426
     {
427
+        /*
428
+
429
+        Note: the browser may not report a new unique pointer (phase: "began")
430
+        as primary. This logic makes trackables[0] primary, or sort of primary.
431
+
432
+        Behavior on Chrome 130 on Android: when moving multiple touch points,
433
+        remove focus from the browser. Touch points will be canceled as
434
+        expected. When touching the screen again with a single finger, the
435
+        (only one) registered pointer will not be primary. That's undesirable.
436
+        Touching the screen again with multiple fingers (none will be primary),
437
+        and then releasing them, will restore the desired behavior.
438
+
439
+        */
440
+
347 441
         // nothing to do
348 442
         if(trackables.length <= 1 || trackables[0].isPrimary)
349 443
             return trackables;
@@ -366,7 +460,7 @@ export class PointerTracker implements Tracker
366 460
      * Find trackables to remove
367 461
      * @returns a list of trackables to remove
368 462
      */
369
-    private _findUnwantedTrackables(): TrackablePointer[]
463
+    private _findInactiveTrackables(): TrackablePointer[]
370 464
     {
371 465
         const trackables: TrackablePointer[] = [];
372 466
 

+ 1
- 1
src/trackers/pointer-tracker/trackable-pointer.ts Parādīt failu

@@ -29,7 +29,7 @@ import { Vector2 } from '../../geometry/vector2';
29 29
  * - "stationary": the user did not move the pointer in this frame
30 30
  * - "moved": the user moved the pointer in this frame
31 31
  * - "ended": the tracking ended in this frame (e.g., a finger has just been lifted from the screen)
32
- * - "canceled": the tracking was canceled in this frame (e.g., the screen orientation of the device has just been changed)
32
+ * - "canceled": the tracking was canceled in this frame (e.g., the page has just lost focus)
33 33
  */
34 34
 export type TrackablePointerPhase = 'began' | 'moved' | 'stationary' | 'ended' | 'canceled';
35 35
 

Notiek ielāde…
Atcelt
Saglabāt