@eva/plugin-worker 2.0.1-beta.26 → 2.0.1-beta.28
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/dist/plugin-worker.cjs.js +1308 -3
- package/dist/plugin-worker.esm.js +1308 -3
- package/package.json +2 -2
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
import { Ticker, UPDATE_PRIORITY, Point, warn, ExtensionType, extensions, Container, DOMAdapter, WebWorkerAdapter } from 'pixi.js';
|
|
2
2
|
import EventEmitter from 'eventemitter3';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* 事件时钟类
|
|
6
|
+
*
|
|
7
|
+
* EventsTicker 用于在指针静止时自动触发指针事件,
|
|
8
|
+
* 确保即使指针不移动,移动的对象也能正确触发悬停测试。
|
|
9
|
+
* 这对于实现动态对象的鼠标交互至关重要。
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* EventsTicker.init(eventSystem);
|
|
14
|
+
* EventsTicker.addTickerListener();
|
|
15
|
+
* EventsTicker.pointerMoved(); // 标记指针已移动
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @since 7.2.0
|
|
19
|
+
*/
|
|
4
20
|
class EventsTickerClass {
|
|
5
21
|
constructor() {
|
|
22
|
+
/** 触发模拟事件的频率(毫秒) */
|
|
6
23
|
this.interactionFrequency = 10;
|
|
7
24
|
this._deltaTime = 0;
|
|
8
25
|
this._didMove = false;
|
|
9
26
|
this._tickerAdded = false;
|
|
10
27
|
this._pauseUpdate = true;
|
|
11
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Initializes the event ticker.
|
|
31
|
+
* @param events - The event system.
|
|
32
|
+
*/
|
|
12
33
|
init(events) {
|
|
13
34
|
this.removeTickerListener();
|
|
14
35
|
this.events = events;
|
|
@@ -18,12 +39,14 @@ class EventsTickerClass {
|
|
|
18
39
|
this._tickerAdded = false;
|
|
19
40
|
this._pauseUpdate = true;
|
|
20
41
|
}
|
|
42
|
+
/** Whether to pause the update checks or not. */
|
|
21
43
|
get pauseUpdate() {
|
|
22
44
|
return this._pauseUpdate;
|
|
23
45
|
}
|
|
24
46
|
set pauseUpdate(paused) {
|
|
25
47
|
this._pauseUpdate = paused;
|
|
26
48
|
}
|
|
49
|
+
/** Adds the ticker listener. */
|
|
27
50
|
addTickerListener() {
|
|
28
51
|
if (this._tickerAdded || !this.domElement) {
|
|
29
52
|
return;
|
|
@@ -31,6 +54,7 @@ class EventsTickerClass {
|
|
|
31
54
|
Ticker.system.add(this._tickerUpdate, this, UPDATE_PRIORITY.INTERACTION);
|
|
32
55
|
this._tickerAdded = true;
|
|
33
56
|
}
|
|
57
|
+
/** Removes the ticker listener. */
|
|
34
58
|
removeTickerListener() {
|
|
35
59
|
if (!this._tickerAdded) {
|
|
36
60
|
return;
|
|
@@ -38,17 +62,21 @@ class EventsTickerClass {
|
|
|
38
62
|
Ticker.system.remove(this._tickerUpdate, this);
|
|
39
63
|
this._tickerAdded = false;
|
|
40
64
|
}
|
|
65
|
+
/** Sets flag to not fire extra events when the user has already moved there mouse */
|
|
41
66
|
pointerMoved() {
|
|
42
67
|
this._didMove = true;
|
|
43
68
|
}
|
|
69
|
+
/** Updates the state of interactive objects. */
|
|
44
70
|
_update() {
|
|
45
71
|
if (!this.domElement || this._pauseUpdate) {
|
|
46
72
|
return;
|
|
47
73
|
}
|
|
74
|
+
// if the user move the mouse this check has already been done using the mouse move!
|
|
48
75
|
if (this._didMove) {
|
|
49
76
|
this._didMove = false;
|
|
50
77
|
return;
|
|
51
78
|
}
|
|
79
|
+
// eslint-disable-next-line dot-notation
|
|
52
80
|
const rootPointerEvent = this.events['_rootPointerEvent'];
|
|
53
81
|
if (this.events.supportsTouchEvents && rootPointerEvent.pointerType === 'touch') {
|
|
54
82
|
return;
|
|
@@ -60,6 +88,13 @@ class EventsTickerClass {
|
|
|
60
88
|
pointerId: rootPointerEvent.pointerId,
|
|
61
89
|
}));
|
|
62
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Updates the state of interactive objects if at least {@link interactionFrequency}
|
|
93
|
+
* milliseconds have passed since the last invocation.
|
|
94
|
+
*
|
|
95
|
+
* Invoked by a throttled ticker update from {@link Ticker.system}.
|
|
96
|
+
* @param ticker - The throttled ticker.
|
|
97
|
+
*/
|
|
63
98
|
_tickerUpdate(ticker) {
|
|
64
99
|
this._deltaTime += ticker.deltaTime;
|
|
65
100
|
if (this._deltaTime < this.interactionFrequency) {
|
|
@@ -71,17 +106,64 @@ class EventsTickerClass {
|
|
|
71
106
|
}
|
|
72
107
|
const EventsTicker = new EventsTickerClass();
|
|
73
108
|
|
|
109
|
+
/**
|
|
110
|
+
* 联合事件类
|
|
111
|
+
*
|
|
112
|
+
* FederatedEvent 是一个兼容 DOM 的合成事件实现,
|
|
113
|
+
* 代表原始的 FederatedEvent 或原生 DOM 事件进行传播。
|
|
114
|
+
* 它提供了统一的事件接口,抹平了不同浏览器和设备之间的差异。
|
|
115
|
+
*
|
|
116
|
+
* 主要特性:
|
|
117
|
+
* - 兼容 DOM Event API
|
|
118
|
+
* - 支持事件冒泡和捕获
|
|
119
|
+
* - 提供事件传播控制
|
|
120
|
+
* - 记录事件路径
|
|
121
|
+
*
|
|
122
|
+
* @typeParam N - 持有的原生事件类型
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* sprite.on('pointerdown', (event: FederatedPointerEvent) => {
|
|
127
|
+
* console.log('Clicked at:', event.global.x, event.global.y);
|
|
128
|
+
* event.stopPropagation(); // 停止事件传播
|
|
129
|
+
* });
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
74
132
|
class FederatedEvent {
|
|
133
|
+
/**
|
|
134
|
+
* @param manager - The event boundary which manages this event. Propagation can only occur
|
|
135
|
+
* within the boundary's jurisdiction.
|
|
136
|
+
*/
|
|
75
137
|
constructor(manager) {
|
|
138
|
+
/** 事件是否冒泡(仅在传播前设置有效) */
|
|
76
139
|
this.bubbles = true;
|
|
140
|
+
/** @deprecated 自 7.0.0 起弃用 */
|
|
77
141
|
this.cancelBubble = true;
|
|
142
|
+
/**
|
|
143
|
+
* 事件是否可以被取消
|
|
144
|
+
* @readonly
|
|
145
|
+
*/
|
|
78
146
|
this.cancelable = false;
|
|
147
|
+
/**
|
|
148
|
+
* Flag added for compatibility with DOM {@code Event}. It is not used in the Federated Events
|
|
149
|
+
* API.
|
|
150
|
+
* @see https://dom.spec.whatwg.org/#dom-event-composed
|
|
151
|
+
*/
|
|
79
152
|
this.composed = false;
|
|
153
|
+
/** Flags whether the default response of the user agent was prevent through this event. */
|
|
80
154
|
this.defaultPrevented = false;
|
|
155
|
+
/**
|
|
156
|
+
* The propagation phase.
|
|
157
|
+
* @default {@link FederatedEvent.NONE}
|
|
158
|
+
*/
|
|
81
159
|
this.eventPhase = FederatedEvent.prototype.NONE;
|
|
160
|
+
/** Flags whether propagation was stopped. */
|
|
82
161
|
this.propagationStopped = false;
|
|
162
|
+
/** Flags whether propagation was immediately stopped. */
|
|
83
163
|
this.propagationImmediatelyStopped = false;
|
|
164
|
+
/** The coordinates of the event relative to the nearest DOM layer. This is a non-standard property. */
|
|
84
165
|
this.layer = new Point();
|
|
166
|
+
/** The coordinates of the event relative to the DOM document. This is a non-standard property. */
|
|
85
167
|
this.page = new Point();
|
|
86
168
|
this.NONE = 0;
|
|
87
169
|
this.CAPTURING_PHASE = 1;
|
|
@@ -89,147 +171,373 @@ class FederatedEvent {
|
|
|
89
171
|
this.BUBBLING_PHASE = 3;
|
|
90
172
|
this.manager = manager;
|
|
91
173
|
}
|
|
174
|
+
/** @readonly */
|
|
92
175
|
get layerX() {
|
|
93
176
|
return this.layer.x;
|
|
94
177
|
}
|
|
178
|
+
/** @readonly */
|
|
95
179
|
get layerY() {
|
|
96
180
|
return this.layer.y;
|
|
97
181
|
}
|
|
182
|
+
/** @readonly */
|
|
98
183
|
get pageX() {
|
|
99
184
|
return this.page.x;
|
|
100
185
|
}
|
|
186
|
+
/** @readonly */
|
|
101
187
|
get pageY() {
|
|
102
188
|
return this.page.y;
|
|
103
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Fallback for the deprecated @code{InteractionEvent.data}.
|
|
192
|
+
* @deprecated since 7.0.0
|
|
193
|
+
*/
|
|
104
194
|
get data() {
|
|
105
195
|
return this;
|
|
106
196
|
}
|
|
197
|
+
/** The propagation path for this event. Alias for {@link EventBoundary.propagationPath}. */
|
|
107
198
|
composedPath() {
|
|
199
|
+
// Find the propagation path if it isn't cached or if the target has changed since since
|
|
200
|
+
// the last evaluation.
|
|
108
201
|
if (this.manager && (!this.path || this.path[this.path.length - 1] !== this.target)) {
|
|
109
202
|
this.path = this.target ? this.manager.propagationPath(this.target) : [];
|
|
110
203
|
}
|
|
111
204
|
return this.path;
|
|
112
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Unimplemented method included for implementing the DOM interface {@code Event}. It will throw an {@code Error}.
|
|
208
|
+
* @deprecated
|
|
209
|
+
* @param _type
|
|
210
|
+
* @param _bubbles
|
|
211
|
+
* @param _cancelable
|
|
212
|
+
*/
|
|
113
213
|
initEvent(_type, _bubbles, _cancelable) {
|
|
114
214
|
throw new Error('initEvent() is a legacy DOM API. It is not implemented in the Federated Events API.');
|
|
115
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Unimplemented method included for implementing the DOM interface {@code UIEvent}. It will throw an {@code Error}.
|
|
218
|
+
* @deprecated
|
|
219
|
+
* @param _typeArg
|
|
220
|
+
* @param _bubblesArg
|
|
221
|
+
* @param _cancelableArg
|
|
222
|
+
* @param _viewArg
|
|
223
|
+
* @param _detailArg
|
|
224
|
+
*/
|
|
116
225
|
initUIEvent(_typeArg, _bubblesArg, _cancelableArg, _viewArg, _detailArg) {
|
|
117
226
|
throw new Error('initUIEvent() is a legacy DOM API. It is not implemented in the Federated Events API.');
|
|
118
227
|
}
|
|
228
|
+
/** Prevent default behavior of PixiJS and the user agent. */
|
|
119
229
|
preventDefault() {
|
|
120
230
|
if (this.nativeEvent instanceof Event && this.nativeEvent.cancelable) {
|
|
121
231
|
this.nativeEvent.preventDefault();
|
|
122
232
|
}
|
|
123
233
|
this.defaultPrevented = true;
|
|
124
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Stop this event from propagating to any addition listeners, including on the
|
|
237
|
+
* {@link FederatedEventTarget.currentTarget currentTarget} and also the following
|
|
238
|
+
* event targets on the propagation path.
|
|
239
|
+
*/
|
|
125
240
|
stopImmediatePropagation() {
|
|
126
241
|
this.propagationImmediatelyStopped = true;
|
|
127
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Stop this event from propagating to the next {@link FederatedEventTarget}. The rest of the listeners
|
|
245
|
+
* on the {@link FederatedEventTarget.currentTarget currentTarget} will still be notified.
|
|
246
|
+
*/
|
|
128
247
|
stopPropagation() {
|
|
129
248
|
this.propagationStopped = true;
|
|
130
249
|
}
|
|
131
250
|
}
|
|
132
251
|
|
|
252
|
+
/**
|
|
253
|
+
* A {@link FederatedEvent} for mouse events.
|
|
254
|
+
* @memberof events
|
|
255
|
+
*/
|
|
133
256
|
class FederatedMouseEvent extends FederatedEvent {
|
|
134
257
|
constructor() {
|
|
135
258
|
super(...arguments);
|
|
259
|
+
/** The coordinates of the mouse event relative to the canvas. */
|
|
136
260
|
this.client = new Point();
|
|
261
|
+
/** The movement in this pointer relative to the last `mousemove` event. */
|
|
137
262
|
this.movement = new Point();
|
|
263
|
+
/** The offset of the pointer coordinates w.r.t. target Container in world space. This is not supported at the moment. */
|
|
138
264
|
this.offset = new Point();
|
|
265
|
+
/** The pointer coordinates in world space. */
|
|
139
266
|
this.global = new Point();
|
|
267
|
+
/**
|
|
268
|
+
* The pointer coordinates in the renderer's {@link Renderer.screen screen}. This has slightly
|
|
269
|
+
* different semantics than native PointerEvent screenX/screenY.
|
|
270
|
+
*/
|
|
140
271
|
this.screen = new Point();
|
|
141
272
|
}
|
|
273
|
+
/** @readonly */
|
|
142
274
|
get clientX() {
|
|
143
275
|
return this.client.x;
|
|
144
276
|
}
|
|
277
|
+
/** @readonly */
|
|
145
278
|
get clientY() {
|
|
146
279
|
return this.client.y;
|
|
147
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Alias for {@link FederatedMouseEvent.clientX this.clientX}.
|
|
283
|
+
* @readonly
|
|
284
|
+
*/
|
|
148
285
|
get x() {
|
|
149
286
|
return this.clientX;
|
|
150
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Alias for {@link FederatedMouseEvent.clientY this.clientY}.
|
|
290
|
+
* @readonly
|
|
291
|
+
*/
|
|
151
292
|
get y() {
|
|
152
293
|
return this.clientY;
|
|
153
294
|
}
|
|
295
|
+
/** @readonly */
|
|
154
296
|
get movementX() {
|
|
155
297
|
return this.movement.x;
|
|
156
298
|
}
|
|
299
|
+
/** @readonly */
|
|
157
300
|
get movementY() {
|
|
158
301
|
return this.movement.y;
|
|
159
302
|
}
|
|
303
|
+
/** @readonly */
|
|
160
304
|
get offsetX() {
|
|
161
305
|
return this.offset.x;
|
|
162
306
|
}
|
|
307
|
+
/** @readonly */
|
|
163
308
|
get offsetY() {
|
|
164
309
|
return this.offset.y;
|
|
165
310
|
}
|
|
311
|
+
/** @readonly */
|
|
166
312
|
get globalX() {
|
|
167
313
|
return this.global.x;
|
|
168
314
|
}
|
|
315
|
+
/** @readonly */
|
|
169
316
|
get globalY() {
|
|
170
317
|
return this.global.y;
|
|
171
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* The pointer coordinates in the renderer's screen. Alias for {@code screen.x}.
|
|
321
|
+
* @readonly
|
|
322
|
+
*/
|
|
172
323
|
get screenX() {
|
|
173
324
|
return this.screen.x;
|
|
174
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* The pointer coordinates in the renderer's screen. Alias for {@code screen.y}.
|
|
328
|
+
* @readonly
|
|
329
|
+
*/
|
|
175
330
|
get screenY() {
|
|
176
331
|
return this.screen.y;
|
|
177
332
|
}
|
|
333
|
+
/**
|
|
334
|
+
* This will return the local coordinates of the specified container for this InteractionData
|
|
335
|
+
* @param {Container} container - The Container that you would like the local
|
|
336
|
+
* coords off
|
|
337
|
+
* @param {PointData} point - A Point object in which to store the value, optional (otherwise
|
|
338
|
+
* will create a new point)
|
|
339
|
+
* @param {PointData} globalPos - A Point object containing your custom global coords, optional
|
|
340
|
+
* (otherwise will use the current global coords)
|
|
341
|
+
* @returns - A point containing the coordinates of the InteractionData position relative
|
|
342
|
+
* to the Container
|
|
343
|
+
*/
|
|
178
344
|
getLocalPosition(container, point, globalPos) {
|
|
179
345
|
return container.worldTransform.applyInverse(globalPos || this.global, point);
|
|
180
346
|
}
|
|
347
|
+
/**
|
|
348
|
+
* Whether the modifier key was pressed when this event natively occurred.
|
|
349
|
+
* @param key - The modifier key.
|
|
350
|
+
*/
|
|
181
351
|
getModifierState(key) {
|
|
182
352
|
return 'getModifierState' in this.nativeEvent && this.nativeEvent.getModifierState(key);
|
|
183
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Not supported.
|
|
356
|
+
* @param _typeArg
|
|
357
|
+
* @param _canBubbleArg
|
|
358
|
+
* @param _cancelableArg
|
|
359
|
+
* @param _viewArg
|
|
360
|
+
* @param _detailArg
|
|
361
|
+
* @param _screenXArg
|
|
362
|
+
* @param _screenYArg
|
|
363
|
+
* @param _clientXArg
|
|
364
|
+
* @param _clientYArg
|
|
365
|
+
* @param _ctrlKeyArg
|
|
366
|
+
* @param _altKeyArg
|
|
367
|
+
* @param _shiftKeyArg
|
|
368
|
+
* @param _metaKeyArg
|
|
369
|
+
* @param _buttonArg
|
|
370
|
+
* @param _relatedTargetArg
|
|
371
|
+
* @deprecated since 7.0.0
|
|
372
|
+
*/
|
|
373
|
+
// eslint-disable-next-line max-params
|
|
184
374
|
initMouseEvent(_typeArg, _canBubbleArg, _cancelableArg, _viewArg, _detailArg, _screenXArg, _screenYArg, _clientXArg, _clientYArg, _ctrlKeyArg, _altKeyArg, _shiftKeyArg, _metaKeyArg, _buttonArg, _relatedTargetArg) {
|
|
185
375
|
throw new Error('Method not implemented.');
|
|
186
376
|
}
|
|
187
377
|
}
|
|
188
378
|
|
|
379
|
+
/**
|
|
380
|
+
* A {@link FederatedEvent} for pointer events.
|
|
381
|
+
* @memberof events
|
|
382
|
+
*/
|
|
189
383
|
class FederatedPointerEvent extends FederatedMouseEvent {
|
|
190
384
|
constructor() {
|
|
191
385
|
super(...arguments);
|
|
386
|
+
/**
|
|
387
|
+
* The width of the pointer's contact along the x-axis, measured in CSS pixels.
|
|
388
|
+
* radiusX of TouchEvents will be represented by this value.
|
|
389
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
|
|
390
|
+
*/
|
|
192
391
|
this.width = 0;
|
|
392
|
+
/**
|
|
393
|
+
* The height of the pointer's contact along the y-axis, measured in CSS pixels.
|
|
394
|
+
* radiusY of TouchEvents will be represented by this value.
|
|
395
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
|
|
396
|
+
*/
|
|
193
397
|
this.height = 0;
|
|
398
|
+
/**
|
|
399
|
+
* Indicates whether or not the pointer device that created the event is the primary pointer.
|
|
400
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
|
|
401
|
+
*/
|
|
194
402
|
this.isPrimary = false;
|
|
195
403
|
}
|
|
404
|
+
// Only included for completeness for now
|
|
196
405
|
getCoalescedEvents() {
|
|
197
406
|
if (this.type === 'pointermove' || this.type === 'mousemove' || this.type === 'touchmove') {
|
|
198
407
|
return [this];
|
|
199
408
|
}
|
|
200
409
|
return [];
|
|
201
410
|
}
|
|
411
|
+
// Only included for completeness for now
|
|
202
412
|
getPredictedEvents() {
|
|
203
413
|
throw new Error('getPredictedEvents is not supported!');
|
|
204
414
|
}
|
|
205
415
|
}
|
|
206
416
|
|
|
417
|
+
/**
|
|
418
|
+
* A {@link FederatedEvent} for wheel events.
|
|
419
|
+
* @memberof events
|
|
420
|
+
*/
|
|
207
421
|
class FederatedWheelEvent extends FederatedMouseEvent {
|
|
208
422
|
constructor() {
|
|
209
423
|
super(...arguments);
|
|
424
|
+
/** Units specified in pixels. */
|
|
210
425
|
this.DOM_DELTA_PIXEL = 0;
|
|
426
|
+
/** Units specified in lines. */
|
|
211
427
|
this.DOM_DELTA_LINE = 1;
|
|
428
|
+
/** Units specified in pages. */
|
|
212
429
|
this.DOM_DELTA_PAGE = 2;
|
|
213
430
|
}
|
|
214
431
|
}
|
|
432
|
+
/** Units specified in pixels. */
|
|
215
433
|
FederatedWheelEvent.DOM_DELTA_PIXEL = 0;
|
|
434
|
+
/** Units specified in lines. */
|
|
216
435
|
FederatedWheelEvent.DOM_DELTA_LINE = 1;
|
|
436
|
+
/** Units specified in pages. */
|
|
217
437
|
FederatedWheelEvent.DOM_DELTA_PAGE = 2;
|
|
218
438
|
|
|
439
|
+
// The maximum iterations used in propagation. This prevent infinite loops.
|
|
219
440
|
const PROPAGATION_LIMIT = 2048;
|
|
220
441
|
const tempHitLocation = new Point();
|
|
221
442
|
const tempLocalMapping = new Point();
|
|
443
|
+
/**
|
|
444
|
+
* Event boundaries are "barriers" where events coming from an upstream scene are modified before downstream propagation.
|
|
445
|
+
*
|
|
446
|
+
* ## Root event boundary
|
|
447
|
+
*
|
|
448
|
+
* The {@link EventSystem#rootBoundary rootBoundary} handles events coming from the <canvas />.
|
|
449
|
+
* {@link EventSystem} handles the normalization from native {@link https://dom.spec.whatwg.org/#event Events}
|
|
450
|
+
* into {@link FederatedEvent FederatedEvents}. The rootBoundary then does the hit-testing and event dispatch
|
|
451
|
+
* for the upstream normalized event.
|
|
452
|
+
*
|
|
453
|
+
* ## Additional event boundaries
|
|
454
|
+
*
|
|
455
|
+
* An additional event boundary may be desired within an application's scene graph. For example, if a portion of the scene is
|
|
456
|
+
* is flat with many children at one level - a spatial hash maybe needed to accelerate hit testing. In this scenario, the
|
|
457
|
+
* container can be detached from the scene and glued using a custom event boundary.
|
|
458
|
+
*
|
|
459
|
+
* ```ts
|
|
460
|
+
* import { Container } from 'pixi.js';
|
|
461
|
+
* import { EventBoundary } from 'pixi.js';
|
|
462
|
+
* import { SpatialHash } from 'pixi-spatial-hash';
|
|
463
|
+
*
|
|
464
|
+
* class HashedHitTestingEventBoundary
|
|
465
|
+
* {
|
|
466
|
+
* private spatialHash: SpatialHash;
|
|
467
|
+
*
|
|
468
|
+
* constructor(scene: Container, spatialHash: SpatialHash)
|
|
469
|
+
* {
|
|
470
|
+
* super(scene);
|
|
471
|
+
* this.spatialHash = spatialHash;
|
|
472
|
+
* }
|
|
473
|
+
*
|
|
474
|
+
* hitTestRecursive(...)
|
|
475
|
+
* {
|
|
476
|
+
* // TODO: If target === this.rootTarget, then use spatial hash to get a
|
|
477
|
+
* // list of possible children that match the given (x,y) coordinates.
|
|
478
|
+
* }
|
|
479
|
+
* }
|
|
480
|
+
*
|
|
481
|
+
* class VastScene extends Container
|
|
482
|
+
* {
|
|
483
|
+
* protected eventBoundary: EventBoundary;
|
|
484
|
+
* protected scene: Container;
|
|
485
|
+
* protected spatialHash: SpatialHash;
|
|
486
|
+
*
|
|
487
|
+
* constructor()
|
|
488
|
+
* {
|
|
489
|
+
* this.scene = new Container();
|
|
490
|
+
* this.spatialHash = new SpatialHash();
|
|
491
|
+
* this.eventBoundary = new HashedHitTestingEventBoundary(this.scene, this.spatialHash);
|
|
492
|
+
*
|
|
493
|
+
* // Populate this.scene with a ton of children, while updating this.spatialHash
|
|
494
|
+
* }
|
|
495
|
+
* }
|
|
496
|
+
* ```
|
|
497
|
+
* @memberof events
|
|
498
|
+
*/
|
|
222
499
|
class EventBoundary {
|
|
500
|
+
/**
|
|
501
|
+
* @param rootTarget - The holder of the event boundary.
|
|
502
|
+
*/
|
|
223
503
|
constructor(rootTarget) {
|
|
504
|
+
/**
|
|
505
|
+
* Emits events after they were dispatched into the scene graph.
|
|
506
|
+
*
|
|
507
|
+
* This can be used for global events listening, regardless of the scene graph being used. It should
|
|
508
|
+
* not be used by interactive libraries for normal use.
|
|
509
|
+
*
|
|
510
|
+
* Special events that do not bubble all the way to the root target are not emitted from here,
|
|
511
|
+
* e.g. pointerenter, pointerleave, click.
|
|
512
|
+
*/
|
|
224
513
|
this.dispatch = new EventEmitter();
|
|
514
|
+
/**
|
|
515
|
+
* This flag would emit `pointermove`, `touchmove`, and `mousemove` events on all Containers.
|
|
516
|
+
*
|
|
517
|
+
* The `moveOnAll` semantics mirror those of earlier versions of PixiJS. This was disabled in favor of
|
|
518
|
+
* the Pointer Event API's approach.
|
|
519
|
+
*/
|
|
225
520
|
this.moveOnAll = false;
|
|
521
|
+
/** Enables the global move events. `globalpointermove`, `globaltouchmove`, and `globalmousemove` */
|
|
226
522
|
this.enableGlobalMoveEvents = true;
|
|
523
|
+
/**
|
|
524
|
+
* State object for mapping methods.
|
|
525
|
+
* @see EventBoundary#trackingData
|
|
526
|
+
*/
|
|
227
527
|
this.mappingState = {
|
|
228
528
|
trackingData: {},
|
|
229
529
|
};
|
|
530
|
+
/**
|
|
531
|
+
* The event pool maps event constructors to an free pool of instances of those specific events.
|
|
532
|
+
* @see EventBoundary#allocateEvent
|
|
533
|
+
* @see EventBoundary#freeEvent
|
|
534
|
+
*/
|
|
230
535
|
this.eventPool = new Map();
|
|
536
|
+
/** Every interactive element gathered from the scene. Only used in `pointermove` */
|
|
231
537
|
this._allInteractiveElements = [];
|
|
538
|
+
/** Every element that passed the hit test. Only used in `pointermove` */
|
|
232
539
|
this._hitElements = [];
|
|
540
|
+
/** Whether or not to collect all the interactive elements from the scene. Enabled in `pointermove` */
|
|
233
541
|
this._isPointerMoveEvent = false;
|
|
234
542
|
this.rootTarget = rootTarget;
|
|
235
543
|
this.hitPruneFn = this.hitPruneFn.bind(this);
|
|
@@ -251,6 +559,18 @@ class EventBoundary {
|
|
|
251
559
|
this.addEventMapping('pointerupoutside', this.mapPointerUpOutside);
|
|
252
560
|
this.addEventMapping('wheel', this.mapWheel);
|
|
253
561
|
}
|
|
562
|
+
/**
|
|
563
|
+
* Adds an event mapping for the event `type` handled by `fn`.
|
|
564
|
+
*
|
|
565
|
+
* Event mappings can be used to implement additional or custom events. They take an event
|
|
566
|
+
* coming from the upstream scene (or directly from the {@link EventSystem}) and dispatch new downstream events
|
|
567
|
+
* generally trickling down and bubbling up to {@link EventBoundary.rootTarget this.rootTarget}.
|
|
568
|
+
*
|
|
569
|
+
* To modify the semantics of existing events, the built-in mapping methods of EventBoundary should be overridden
|
|
570
|
+
* instead.
|
|
571
|
+
* @param type - The type of upstream event to map.
|
|
572
|
+
* @param fn - The mapping method. The context of this function must be bound manually, if desired.
|
|
573
|
+
*/
|
|
254
574
|
addEventMapping(type, fn) {
|
|
255
575
|
if (!this.mappingTable[type]) {
|
|
256
576
|
this.mappingTable[type] = [];
|
|
@@ -261,12 +581,21 @@ class EventBoundary {
|
|
|
261
581
|
});
|
|
262
582
|
this.mappingTable[type].sort((a, b) => a.priority - b.priority);
|
|
263
583
|
}
|
|
584
|
+
/**
|
|
585
|
+
* Dispatches the given event
|
|
586
|
+
* @param e - The event to dispatch.
|
|
587
|
+
* @param type - The type of event to dispatch. Defaults to `e.type`.
|
|
588
|
+
*/
|
|
264
589
|
dispatchEvent(e, type) {
|
|
265
590
|
e.propagationStopped = false;
|
|
266
591
|
e.propagationImmediatelyStopped = false;
|
|
267
592
|
this.propagate(e, type);
|
|
268
593
|
this.dispatch.emit(type || e.type, e);
|
|
269
594
|
}
|
|
595
|
+
/**
|
|
596
|
+
* Maps the given upstream event through the event boundary and propagates it downstream.
|
|
597
|
+
* @param e - The event to map.
|
|
598
|
+
*/
|
|
270
599
|
mapEvent(e) {
|
|
271
600
|
if (!this.rootTarget) {
|
|
272
601
|
return;
|
|
@@ -278,21 +607,39 @@ class EventBoundary {
|
|
|
278
607
|
}
|
|
279
608
|
}
|
|
280
609
|
else {
|
|
610
|
+
// #if _DEBUG
|
|
281
611
|
warn(`[EventBoundary]: Event mapping not defined for ${e.type}`);
|
|
612
|
+
// #endif
|
|
282
613
|
}
|
|
283
614
|
}
|
|
615
|
+
/**
|
|
616
|
+
* Finds the Container that is the target of a event at the given coordinates.
|
|
617
|
+
*
|
|
618
|
+
* The passed (x,y) coordinates are in the world space above this event boundary.
|
|
619
|
+
* @param x - The x coordinate of the event.
|
|
620
|
+
* @param y - The y coordinate of the event.
|
|
621
|
+
*/
|
|
284
622
|
hitTest(x, y) {
|
|
285
623
|
EventsTicker.pauseUpdate = true;
|
|
624
|
+
// if we are using global move events, we need to hit test the whole scene graph
|
|
286
625
|
const useMove = this._isPointerMoveEvent && this.enableGlobalMoveEvents;
|
|
287
626
|
const fn = useMove ? 'hitTestMoveRecursive' : 'hitTestRecursive';
|
|
288
627
|
const invertedPath = this[fn](this.rootTarget, this.rootTarget.eventMode, tempHitLocation.set(x, y), this.hitTestFn, this.hitPruneFn);
|
|
289
628
|
return invertedPath && invertedPath[0];
|
|
290
629
|
}
|
|
630
|
+
/**
|
|
631
|
+
* Propagate the passed event from from {@link EventBoundary.rootTarget this.rootTarget} to its
|
|
632
|
+
* target {@code e.target}.
|
|
633
|
+
* @param e - The event to propagate.
|
|
634
|
+
* @param type - The type of event to propagate. Defaults to `e.type`.
|
|
635
|
+
*/
|
|
291
636
|
propagate(e, type) {
|
|
292
637
|
if (!e.target) {
|
|
638
|
+
// This usually occurs when the scene graph is not interactive.
|
|
293
639
|
return;
|
|
294
640
|
}
|
|
295
641
|
const composedPath = e.composedPath();
|
|
642
|
+
// Capturing phase
|
|
296
643
|
e.eventPhase = e.CAPTURING_PHASE;
|
|
297
644
|
for (let i = 0, j = composedPath.length - 1; i < j; i++) {
|
|
298
645
|
e.currentTarget = composedPath[i];
|
|
@@ -300,11 +647,13 @@ class EventBoundary {
|
|
|
300
647
|
if (e.propagationStopped || e.propagationImmediatelyStopped)
|
|
301
648
|
return;
|
|
302
649
|
}
|
|
650
|
+
// At target phase
|
|
303
651
|
e.eventPhase = e.AT_TARGET;
|
|
304
652
|
e.currentTarget = e.target;
|
|
305
653
|
this.notifyTarget(e, type);
|
|
306
654
|
if (e.propagationStopped || e.propagationImmediatelyStopped)
|
|
307
655
|
return;
|
|
656
|
+
// Bubbling phase
|
|
308
657
|
e.eventPhase = e.BUBBLING_PHASE;
|
|
309
658
|
for (let i = composedPath.length - 2; i >= 0; i--) {
|
|
310
659
|
e.currentTarget = composedPath[i];
|
|
@@ -313,11 +662,21 @@ class EventBoundary {
|
|
|
313
662
|
return;
|
|
314
663
|
}
|
|
315
664
|
}
|
|
665
|
+
/**
|
|
666
|
+
* Emits the event {@code e} to all interactive containers. The event is propagated in the bubbling phase always.
|
|
667
|
+
*
|
|
668
|
+
* This is used in the `globalpointermove` event.
|
|
669
|
+
* @param e - The emitted event.
|
|
670
|
+
* @param type - The listeners to notify.
|
|
671
|
+
* @param targets - The targets to notify.
|
|
672
|
+
*/
|
|
316
673
|
all(e, type, targets = this._allInteractiveElements) {
|
|
317
674
|
if (targets.length === 0)
|
|
318
675
|
return;
|
|
319
676
|
e.eventPhase = e.BUBBLING_PHASE;
|
|
320
677
|
const events = Array.isArray(type) ? type : [type];
|
|
678
|
+
// loop through all interactive elements and notify them of the event
|
|
679
|
+
// loop through targets backwards
|
|
321
680
|
for (let i = targets.length - 1; i >= 0; i--) {
|
|
322
681
|
events.forEach(event => {
|
|
323
682
|
e.currentTarget = targets[i];
|
|
@@ -325,6 +684,11 @@ class EventBoundary {
|
|
|
325
684
|
});
|
|
326
685
|
}
|
|
327
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
* Finds the propagation path from {@link EventBoundary.rootTarget rootTarget} to the passed
|
|
689
|
+
* {@code target}. The last element in the path is {@code target}.
|
|
690
|
+
* @param target - The target to find the propagation path to.
|
|
691
|
+
*/
|
|
328
692
|
propagationPath(target) {
|
|
329
693
|
const propagationPath = [target];
|
|
330
694
|
for (let i = 0; i < PROPAGATION_LIMIT && target !== this.rootTarget && target.parent; i++) {
|
|
@@ -339,6 +703,7 @@ class EventBoundary {
|
|
|
339
703
|
}
|
|
340
704
|
hitTestMoveRecursive(currentTarget, eventMode, location, testFn, pruneFn, ignore = false) {
|
|
341
705
|
let shouldReturn = false;
|
|
706
|
+
// only bail out early if it is not interactive
|
|
342
707
|
if (this._interactivePrune(currentTarget))
|
|
343
708
|
return null;
|
|
344
709
|
if (currentTarget.eventMode === 'dynamic' || eventMode === 'dynamic') {
|
|
@@ -350,15 +715,21 @@ class EventBoundary {
|
|
|
350
715
|
const child = children[i];
|
|
351
716
|
const nestedHit = this.hitTestMoveRecursive(child, this._isInteractive(eventMode) ? eventMode : child.eventMode, location, testFn, pruneFn, ignore || pruneFn(currentTarget, location));
|
|
352
717
|
if (nestedHit) {
|
|
718
|
+
// Its a good idea to check if a child has lost its parent.
|
|
719
|
+
// this means it has been removed whilst looping so its best
|
|
353
720
|
if (nestedHit.length > 0 && !nestedHit[nestedHit.length - 1].parent) {
|
|
354
721
|
continue;
|
|
355
722
|
}
|
|
723
|
+
// Only add the current hit-test target to the hit-test chain if the chain
|
|
724
|
+
// has already started (i.e. the event target has been found) or if the current
|
|
725
|
+
// target is interactive (i.e. it becomes the event target).
|
|
356
726
|
const isInteractive = currentTarget.isInteractive();
|
|
357
727
|
if (nestedHit.length > 0 || isInteractive) {
|
|
358
728
|
if (isInteractive)
|
|
359
729
|
this._allInteractiveElements.push(currentTarget);
|
|
360
730
|
nestedHit.push(currentTarget);
|
|
361
731
|
}
|
|
732
|
+
// store all hit elements to be returned once we have traversed the whole tree
|
|
362
733
|
if (this._hitElements.length === 0)
|
|
363
734
|
this._hitElements = nestedHit;
|
|
364
735
|
shouldReturn = true;
|
|
@@ -369,22 +740,43 @@ class EventBoundary {
|
|
|
369
740
|
const isInteractiveTarget = currentTarget.isInteractive();
|
|
370
741
|
if (isInteractiveTarget && isInteractiveTarget)
|
|
371
742
|
this._allInteractiveElements.push(currentTarget);
|
|
743
|
+
// we don't carry on hit testing something once we have found a hit,
|
|
744
|
+
// now only care about gathering the interactive elements
|
|
372
745
|
if (ignore || this._hitElements.length > 0)
|
|
373
746
|
return null;
|
|
374
747
|
if (shouldReturn)
|
|
375
748
|
return this._hitElements;
|
|
749
|
+
// Finally, hit test this Container itself.
|
|
376
750
|
if (isInteractiveMode && !pruneFn(currentTarget, location) && testFn(currentTarget, location)) {
|
|
751
|
+
// The current hit-test target is the event's target only if it is interactive. Otherwise,
|
|
752
|
+
// the first interactive ancestor will be the event's target.
|
|
377
753
|
return isInteractiveTarget ? [currentTarget] : [];
|
|
378
754
|
}
|
|
379
755
|
return null;
|
|
380
756
|
}
|
|
757
|
+
/**
|
|
758
|
+
* Recursive implementation for {@link EventBoundary.hitTest hitTest}.
|
|
759
|
+
* @param currentTarget - The Container that is to be hit tested.
|
|
760
|
+
* @param eventMode - The event mode for the `currentTarget` or one of its parents.
|
|
761
|
+
* @param location - The location that is being tested for overlap.
|
|
762
|
+
* @param testFn - Callback that determines whether the target passes hit testing. This callback
|
|
763
|
+
* can assume that `pruneFn` failed to prune the container.
|
|
764
|
+
* @param pruneFn - Callback that determiness whether the target and all of its children
|
|
765
|
+
* cannot pass the hit test. It is used as a preliminary optimization to prune entire subtrees
|
|
766
|
+
* of the scene graph.
|
|
767
|
+
* @returns An array holding the hit testing target and all its ancestors in order. The first element
|
|
768
|
+
* is the target itself and the last is {@link EventBoundary.rootTarget rootTarget}. This is the opposite
|
|
769
|
+
* order w.r.t. the propagation path. If no hit testing target is found, null is returned.
|
|
770
|
+
*/
|
|
381
771
|
hitTestRecursive(currentTarget, eventMode, location, testFn, pruneFn) {
|
|
772
|
+
// Attempt to prune this Container and its subtree as an optimization.
|
|
382
773
|
if (this._interactivePrune(currentTarget) || pruneFn(currentTarget, location)) {
|
|
383
774
|
return null;
|
|
384
775
|
}
|
|
385
776
|
if (currentTarget.eventMode === 'dynamic' || eventMode === 'dynamic') {
|
|
386
777
|
EventsTicker.pauseUpdate = false;
|
|
387
778
|
}
|
|
779
|
+
// Find a child that passes the hit testing and return one, if any.
|
|
388
780
|
if (currentTarget.interactiveChildren && currentTarget.children) {
|
|
389
781
|
const children = currentTarget.children;
|
|
390
782
|
const relativeLocation = location;
|
|
@@ -392,9 +784,14 @@ class EventBoundary {
|
|
|
392
784
|
const child = children[i];
|
|
393
785
|
const nestedHit = this.hitTestRecursive(child, this._isInteractive(eventMode) ? eventMode : child.eventMode, relativeLocation, testFn, pruneFn);
|
|
394
786
|
if (nestedHit) {
|
|
787
|
+
// Its a good idea to check if a child has lost its parent.
|
|
788
|
+
// this means it has been removed whilst looping so its best
|
|
395
789
|
if (nestedHit.length > 0 && !nestedHit[nestedHit.length - 1].parent) {
|
|
396
790
|
continue;
|
|
397
791
|
}
|
|
792
|
+
// Only add the current hit-test target to the hit-test chain if the chain
|
|
793
|
+
// has already started (i.e. the event target has been found) or if the current
|
|
794
|
+
// target is interactive (i.e. it becomes the event target).
|
|
398
795
|
const isInteractive = currentTarget.isInteractive();
|
|
399
796
|
if (nestedHit.length > 0 || isInteractive)
|
|
400
797
|
nestedHit.push(currentTarget);
|
|
@@ -404,7 +801,10 @@ class EventBoundary {
|
|
|
404
801
|
}
|
|
405
802
|
const isInteractiveMode = this._isInteractive(eventMode);
|
|
406
803
|
const isInteractiveTarget = currentTarget.isInteractive();
|
|
804
|
+
// Finally, hit test this Container itself.
|
|
407
805
|
if (isInteractiveMode && testFn(currentTarget, location)) {
|
|
806
|
+
// The current hit-test target is the event's target only if it is interactive. Otherwise,
|
|
807
|
+
// the first interactive ancestor will be the event's target.
|
|
408
808
|
return isInteractiveTarget ? [currentTarget] : [];
|
|
409
809
|
}
|
|
410
810
|
return null;
|
|
@@ -413,17 +813,28 @@ class EventBoundary {
|
|
|
413
813
|
return int === 'static' || int === 'dynamic';
|
|
414
814
|
}
|
|
415
815
|
_interactivePrune(container) {
|
|
816
|
+
// If container is a mask, invisible, or not renderable then it cannot be hit directly.
|
|
416
817
|
if (!container || !container.visible || !container.renderable || !container.measurable) {
|
|
417
818
|
return true;
|
|
418
819
|
}
|
|
820
|
+
// If this Container is none then it cannot be hit by anything.
|
|
419
821
|
if (container.eventMode === 'none') {
|
|
420
822
|
return true;
|
|
421
823
|
}
|
|
824
|
+
// If this Container is passive and it has no interactive children then it cannot be hit
|
|
422
825
|
if (container.eventMode === 'passive' && !container.interactiveChildren) {
|
|
423
826
|
return true;
|
|
424
827
|
}
|
|
425
828
|
return false;
|
|
426
829
|
}
|
|
830
|
+
/**
|
|
831
|
+
* Checks whether the container or any of its children cannot pass the hit test at all.
|
|
832
|
+
*
|
|
833
|
+
* {@link EventBoundary}'s implementation uses the {@link Container.hitArea hitArea}
|
|
834
|
+
* and {@link Container._maskEffect} for pruning.
|
|
835
|
+
* @param container - The container to prune.
|
|
836
|
+
* @param location - The location to test for overlap.
|
|
837
|
+
*/
|
|
427
838
|
hitPruneFn(container, location) {
|
|
428
839
|
if (container.hitArea) {
|
|
429
840
|
container.worldTransform.applyInverse(location, tempLocalMapping);
|
|
@@ -444,8 +855,15 @@ class EventBoundary {
|
|
|
444
855
|
}
|
|
445
856
|
return false;
|
|
446
857
|
}
|
|
858
|
+
/**
|
|
859
|
+
* Checks whether the container passes hit testing for the given location.
|
|
860
|
+
* @param container - The container to test.
|
|
861
|
+
* @param location - The location to test for overlap.
|
|
862
|
+
* @returns - Whether `container` passes hit testing for `location`.
|
|
863
|
+
*/
|
|
447
864
|
hitTestFn(container, location) {
|
|
448
865
|
var _a;
|
|
866
|
+
// If the container failed pruning with a hitArea, then it must pass it.
|
|
449
867
|
if (container.hitArea) {
|
|
450
868
|
return true;
|
|
451
869
|
}
|
|
@@ -453,14 +871,24 @@ class EventBoundary {
|
|
|
453
871
|
container.worldTransform.applyInverse(location, tempLocalMapping);
|
|
454
872
|
return container.containsPoint(tempLocalMapping);
|
|
455
873
|
}
|
|
874
|
+
// TODO: Should we hit test based on bounds?
|
|
456
875
|
return false;
|
|
457
876
|
}
|
|
877
|
+
/**
|
|
878
|
+
* Notify all the listeners to the event's `currentTarget`.
|
|
879
|
+
*
|
|
880
|
+
* If the `currentTarget` contains the property `on<type>`, then it is called here,
|
|
881
|
+
* simulating the behavior from version 6.x and prior.
|
|
882
|
+
* @param e - The event passed to the target.
|
|
883
|
+
* @param type - The type of event to notify. Defaults to `e.type`.
|
|
884
|
+
*/
|
|
458
885
|
notifyTarget(e, type) {
|
|
459
886
|
var _a;
|
|
460
887
|
if (!e.currentTarget.isInteractive()) {
|
|
461
888
|
return;
|
|
462
889
|
}
|
|
463
890
|
type = type !== null && type !== void 0 ? type : e.type;
|
|
891
|
+
// call the `on${type}` for the current target if it exists
|
|
464
892
|
const handlerKey = `on${type}`;
|
|
465
893
|
(_a = e.currentTarget[handlerKey]) === null || _a === void 0 ? void 0 : _a(e);
|
|
466
894
|
const key = e.eventPhase === e.CAPTURING_PHASE || e.eventPhase === e.AT_TARGET ? `${type}capture` : type;
|
|
@@ -469,9 +897,17 @@ class EventBoundary {
|
|
|
469
897
|
this._notifyListeners(e, type);
|
|
470
898
|
}
|
|
471
899
|
}
|
|
900
|
+
/**
|
|
901
|
+
* Maps the upstream `pointerdown` events to a downstream `pointerdown` event.
|
|
902
|
+
*
|
|
903
|
+
* `touchstart`, `rightdown`, `mousedown` events are also dispatched for specific pointer types.
|
|
904
|
+
* @param from - The upstream `pointerdown` event.
|
|
905
|
+
*/
|
|
472
906
|
mapPointerDown(from) {
|
|
473
907
|
if (!(from instanceof FederatedPointerEvent)) {
|
|
908
|
+
// #if _DEBUG
|
|
474
909
|
warn('EventBoundary cannot map a non-pointer event as a pointer event');
|
|
910
|
+
// #endif
|
|
475
911
|
return;
|
|
476
912
|
}
|
|
477
913
|
const e = this.createPointerEvent(from);
|
|
@@ -487,10 +923,19 @@ class EventBoundary {
|
|
|
487
923
|
trackingData.pressTargetsByButton[from.button] = e.composedPath();
|
|
488
924
|
this.freeEvent(e);
|
|
489
925
|
}
|
|
926
|
+
/**
|
|
927
|
+
* Maps the upstream `pointermove` to downstream `pointerout`, `pointerover`, and `pointermove` events, in that order.
|
|
928
|
+
*
|
|
929
|
+
* The tracking data for the specific pointer has an updated `overTarget`. `mouseout`, `mouseover`,
|
|
930
|
+
* `mousemove`, and `touchmove` events are fired as well for specific pointer types.
|
|
931
|
+
* @param from - The upstream `pointermove` event.
|
|
932
|
+
*/
|
|
490
933
|
mapPointerMove(from) {
|
|
491
934
|
var _a, _b, _c;
|
|
492
935
|
if (!(from instanceof FederatedPointerEvent)) {
|
|
936
|
+
// #if _DEBUG
|
|
493
937
|
warn('EventBoundary cannot map a non-pointer event as a pointer event');
|
|
938
|
+
// #endif
|
|
494
939
|
return;
|
|
495
940
|
}
|
|
496
941
|
this._allInteractiveElements.length = 0;
|
|
@@ -501,12 +946,16 @@ class EventBoundary {
|
|
|
501
946
|
const isMouse = e.pointerType === 'mouse' || e.pointerType === 'pen';
|
|
502
947
|
const trackingData = this.trackingData(from.pointerId);
|
|
503
948
|
const outTarget = this.findMountedTarget(trackingData.overTargets);
|
|
949
|
+
// First pointerout/pointerleave
|
|
504
950
|
if (((_a = trackingData.overTargets) === null || _a === void 0 ? void 0 : _a.length) > 0 && outTarget !== e.target) {
|
|
951
|
+
// pointerout always occurs on the overTarget when the pointer hovers over another element.
|
|
505
952
|
const outType = from.type === 'mousemove' ? 'mouseout' : 'pointerout';
|
|
506
953
|
const outEvent = this.createPointerEvent(from, outType, outTarget);
|
|
507
954
|
this.dispatchEvent(outEvent, 'pointerout');
|
|
508
955
|
if (isMouse)
|
|
509
956
|
this.dispatchEvent(outEvent, 'mouseout');
|
|
957
|
+
// If the pointer exits overTarget and its descendants, then a pointerleave event is also fired. This event
|
|
958
|
+
// is dispatched to all ancestors that no longer capture the pointer.
|
|
510
959
|
if (!e.composedPath().includes(outTarget)) {
|
|
511
960
|
const leaveEvent = this.createPointerEvent(from, 'pointerleave', outTarget);
|
|
512
961
|
leaveEvent.eventPhase = leaveEvent.AT_TARGET;
|
|
@@ -521,18 +970,23 @@ class EventBoundary {
|
|
|
521
970
|
}
|
|
522
971
|
this.freeEvent(outEvent);
|
|
523
972
|
}
|
|
973
|
+
// Then pointerover
|
|
524
974
|
if (outTarget !== e.target) {
|
|
975
|
+
// pointerover always occurs on the new overTarget
|
|
525
976
|
const overType = from.type === 'mousemove' ? 'mouseover' : 'pointerover';
|
|
526
|
-
const overEvent = this.clonePointerEvent(e, overType);
|
|
977
|
+
const overEvent = this.clonePointerEvent(e, overType); // clone faster
|
|
527
978
|
this.dispatchEvent(overEvent, 'pointerover');
|
|
528
979
|
if (isMouse)
|
|
529
980
|
this.dispatchEvent(overEvent, 'mouseover');
|
|
981
|
+
// Probe whether the newly hovered Container is an ancestor of the original overTarget.
|
|
530
982
|
let overTargetAncestor = outTarget === null || outTarget === void 0 ? void 0 : outTarget.parent;
|
|
531
983
|
while (overTargetAncestor && overTargetAncestor !== this.rootTarget.parent) {
|
|
532
984
|
if (overTargetAncestor === e.target)
|
|
533
985
|
break;
|
|
534
986
|
overTargetAncestor = overTargetAncestor.parent;
|
|
535
987
|
}
|
|
988
|
+
// The pointer has entered a non-ancestor of the original overTarget. This means we need a pointerentered
|
|
989
|
+
// event.
|
|
536
990
|
const didPointerEnter = !overTargetAncestor || overTargetAncestor === this.rootTarget.parent;
|
|
537
991
|
if (didPointerEnter) {
|
|
538
992
|
const enterEvent = this.clonePointerEvent(e, 'pointerenter');
|
|
@@ -552,6 +1006,7 @@ class EventBoundary {
|
|
|
552
1006
|
const allowGlobalPointerEvents = (_b = this.enableGlobalMoveEvents) !== null && _b !== void 0 ? _b : true;
|
|
553
1007
|
this.moveOnAll ? allMethods.push('pointermove') : this.dispatchEvent(e, 'pointermove');
|
|
554
1008
|
allowGlobalPointerEvents && allMethods.push('globalpointermove');
|
|
1009
|
+
// Then pointermove
|
|
555
1010
|
if (e.pointerType === 'touch') {
|
|
556
1011
|
this.moveOnAll ? allMethods.splice(1, 0, 'touchmove') : this.dispatchEvent(e, 'touchmove');
|
|
557
1012
|
allowGlobalPointerEvents && allMethods.push('globaltouchmove');
|
|
@@ -569,10 +1024,18 @@ class EventBoundary {
|
|
|
569
1024
|
trackingData.overTargets = e.composedPath();
|
|
570
1025
|
this.freeEvent(e);
|
|
571
1026
|
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Maps the upstream `pointerover` to downstream `pointerover` and `pointerenter` events, in that order.
|
|
1029
|
+
*
|
|
1030
|
+
* The tracking data for the specific pointer gets a new `overTarget`.
|
|
1031
|
+
* @param from - The upstream `pointerover` event.
|
|
1032
|
+
*/
|
|
572
1033
|
mapPointerOver(from) {
|
|
573
1034
|
var _a;
|
|
574
1035
|
if (!(from instanceof FederatedPointerEvent)) {
|
|
1036
|
+
// #if _DEBUG
|
|
575
1037
|
warn('EventBoundary cannot map a non-pointer event as a pointer event');
|
|
1038
|
+
// #endif
|
|
576
1039
|
return;
|
|
577
1040
|
}
|
|
578
1041
|
const trackingData = this.trackingData(from.pointerId);
|
|
@@ -583,6 +1046,7 @@ class EventBoundary {
|
|
|
583
1046
|
this.dispatchEvent(e, 'mouseover');
|
|
584
1047
|
if (e.pointerType === 'mouse')
|
|
585
1048
|
this.cursor = (_a = e.target) === null || _a === void 0 ? void 0 : _a.cursor;
|
|
1049
|
+
// pointerenter events must be fired since the pointer entered from upstream.
|
|
586
1050
|
const enterEvent = this.clonePointerEvent(e, 'pointerenter');
|
|
587
1051
|
enterEvent.eventPhase = enterEvent.AT_TARGET;
|
|
588
1052
|
while (enterEvent.target && enterEvent.target !== this.rootTarget.parent) {
|
|
@@ -596,19 +1060,30 @@ class EventBoundary {
|
|
|
596
1060
|
this.freeEvent(e);
|
|
597
1061
|
this.freeEvent(enterEvent);
|
|
598
1062
|
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Maps the upstream `pointerout` to downstream `pointerout`, `pointerleave` events, in that order.
|
|
1065
|
+
*
|
|
1066
|
+
* The tracking data for the specific pointer is cleared of a `overTarget`.
|
|
1067
|
+
* @param from - The upstream `pointerout` event.
|
|
1068
|
+
*/
|
|
599
1069
|
mapPointerOut(from) {
|
|
600
1070
|
if (!(from instanceof FederatedPointerEvent)) {
|
|
1071
|
+
// #if _DEBUG
|
|
601
1072
|
warn('EventBoundary cannot map a non-pointer event as a pointer event');
|
|
1073
|
+
// #endif
|
|
602
1074
|
return;
|
|
603
1075
|
}
|
|
604
1076
|
const trackingData = this.trackingData(from.pointerId);
|
|
605
1077
|
if (trackingData.overTargets) {
|
|
606
1078
|
const isMouse = from.pointerType === 'mouse' || from.pointerType === 'pen';
|
|
607
1079
|
const outTarget = this.findMountedTarget(trackingData.overTargets);
|
|
1080
|
+
// pointerout first
|
|
608
1081
|
const outEvent = this.createPointerEvent(from, 'pointerout', outTarget);
|
|
609
1082
|
this.dispatchEvent(outEvent);
|
|
610
1083
|
if (isMouse)
|
|
611
1084
|
this.dispatchEvent(outEvent, 'mouseout');
|
|
1085
|
+
// pointerleave(s) are also dispatched b/c the pointer must've left rootTarget and its descendants to
|
|
1086
|
+
// get an upstream pointerout event (upstream events do not know rootTarget has descendants).
|
|
612
1087
|
const leaveEvent = this.createPointerEvent(from, 'pointerleave', outTarget);
|
|
613
1088
|
leaveEvent.eventPhase = leaveEvent.AT_TARGET;
|
|
614
1089
|
while (leaveEvent.target && leaveEvent.target !== this.rootTarget.parent) {
|
|
@@ -624,9 +1099,21 @@ class EventBoundary {
|
|
|
624
1099
|
}
|
|
625
1100
|
this.cursor = null;
|
|
626
1101
|
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Maps the upstream `pointerup` event to downstream `pointerup`, `pointerupoutside`,
|
|
1104
|
+
* and `click`/`rightclick`/`pointertap` events, in that order.
|
|
1105
|
+
*
|
|
1106
|
+
* The `pointerupoutside` event bubbles from the original `pointerdown` target to the most specific
|
|
1107
|
+
* ancestor of the `pointerdown` and `pointerup` targets, which is also the `click` event's target. `touchend`,
|
|
1108
|
+
* `rightup`, `mouseup`, `touchendoutside`, `rightupoutside`, `mouseupoutside`, and `tap` are fired as well for
|
|
1109
|
+
* specific pointer types.
|
|
1110
|
+
* @param from - The upstream `pointerup` event.
|
|
1111
|
+
*/
|
|
627
1112
|
mapPointerUp(from) {
|
|
628
1113
|
if (!(from instanceof FederatedPointerEvent)) {
|
|
1114
|
+
// #if _DEBUG
|
|
629
1115
|
warn('EventBoundary cannot map a non-pointer event as a pointer event');
|
|
1116
|
+
// #endif
|
|
630
1117
|
return;
|
|
631
1118
|
}
|
|
632
1119
|
const now = performance.now();
|
|
@@ -642,6 +1129,8 @@ class EventBoundary {
|
|
|
642
1129
|
const trackingData = this.trackingData(from.pointerId);
|
|
643
1130
|
const pressTarget = this.findMountedTarget(trackingData.pressTargetsByButton[from.button]);
|
|
644
1131
|
let clickTarget = pressTarget;
|
|
1132
|
+
// pointerupoutside only bubbles. It only bubbles upto the parent that doesn't contain
|
|
1133
|
+
// the pointerup location.
|
|
645
1134
|
if (pressTarget && !e.composedPath().includes(pressTarget)) {
|
|
646
1135
|
let currentTarget = pressTarget;
|
|
647
1136
|
while (currentTarget && !e.composedPath().includes(currentTarget)) {
|
|
@@ -657,8 +1146,11 @@ class EventBoundary {
|
|
|
657
1146
|
currentTarget = currentTarget.parent;
|
|
658
1147
|
}
|
|
659
1148
|
delete trackingData.pressTargetsByButton[from.button];
|
|
1149
|
+
// currentTarget is the most specific ancestor holding both the pointerdown and pointerup
|
|
1150
|
+
// targets. That is - it's our click target!
|
|
660
1151
|
clickTarget = currentTarget;
|
|
661
1152
|
}
|
|
1153
|
+
// click!
|
|
662
1154
|
if (clickTarget) {
|
|
663
1155
|
const clickEvent = this.clonePointerEvent(e, 'click');
|
|
664
1156
|
clickEvent.target = clickTarget;
|
|
@@ -692,9 +1184,22 @@ class EventBoundary {
|
|
|
692
1184
|
}
|
|
693
1185
|
this.freeEvent(e);
|
|
694
1186
|
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Maps the upstream `pointerupoutside` event to a downstream `pointerupoutside` event, bubbling from the original
|
|
1189
|
+
* `pointerdown` target to `rootTarget`.
|
|
1190
|
+
*
|
|
1191
|
+
* (The most specific ancestor of the `pointerdown` event and the `pointerup` event must the
|
|
1192
|
+
* `{@link EventBoundary}'s root because the `pointerup` event occurred outside of the boundary.)
|
|
1193
|
+
*
|
|
1194
|
+
* `touchendoutside`, `mouseupoutside`, and `rightupoutside` events are fired as well for specific pointer
|
|
1195
|
+
* types. The tracking data for the specific pointer is cleared of a `pressTarget`.
|
|
1196
|
+
* @param from - The upstream `pointerupoutside` event.
|
|
1197
|
+
*/
|
|
695
1198
|
mapPointerUpOutside(from) {
|
|
696
1199
|
if (!(from instanceof FederatedPointerEvent)) {
|
|
1200
|
+
// #if _DEBUG
|
|
697
1201
|
warn('EventBoundary cannot map a non-pointer event as a pointer event');
|
|
1202
|
+
// #endif
|
|
698
1203
|
return;
|
|
699
1204
|
}
|
|
700
1205
|
const trackingData = this.trackingData(from.pointerId);
|
|
@@ -717,21 +1222,37 @@ class EventBoundary {
|
|
|
717
1222
|
}
|
|
718
1223
|
this.freeEvent(e);
|
|
719
1224
|
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Maps the upstream `wheel` event to a downstream `wheel` event.
|
|
1227
|
+
* @param from - The upstream `wheel` event.
|
|
1228
|
+
*/
|
|
720
1229
|
mapWheel(from) {
|
|
721
1230
|
if (!(from instanceof FederatedWheelEvent)) {
|
|
1231
|
+
// #if _DEBUG
|
|
722
1232
|
warn('EventBoundary cannot map a non-wheel event as a wheel event');
|
|
1233
|
+
// #endif
|
|
723
1234
|
return;
|
|
724
1235
|
}
|
|
725
1236
|
const wheelEvent = this.createWheelEvent(from);
|
|
726
1237
|
this.dispatchEvent(wheelEvent);
|
|
727
1238
|
this.freeEvent(wheelEvent);
|
|
728
1239
|
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Finds the most specific event-target in the given propagation path that is still mounted in the scene graph.
|
|
1242
|
+
*
|
|
1243
|
+
* This is used to find the correct `pointerup` and `pointerout` target in the case that the original `pointerdown`
|
|
1244
|
+
* or `pointerover` target was unmounted from the scene graph.
|
|
1245
|
+
* @param propagationPath - The propagation path was valid in the past.
|
|
1246
|
+
* @returns - The most specific event-target still mounted at the same location in the scene graph.
|
|
1247
|
+
*/
|
|
729
1248
|
findMountedTarget(propagationPath) {
|
|
730
1249
|
if (!propagationPath) {
|
|
731
1250
|
return null;
|
|
732
1251
|
}
|
|
733
1252
|
let currentTarget = propagationPath[0];
|
|
734
1253
|
for (let i = 1; i < propagationPath.length; i++) {
|
|
1254
|
+
// Set currentTarget to the next target in the path only if it is still attached to the
|
|
1255
|
+
// scene graph (i.e. parent still points to the expected ancestor).
|
|
735
1256
|
if (propagationPath[i].parent === currentTarget) {
|
|
736
1257
|
currentTarget = propagationPath[i];
|
|
737
1258
|
}
|
|
@@ -741,6 +1262,14 @@ class EventBoundary {
|
|
|
741
1262
|
}
|
|
742
1263
|
return currentTarget;
|
|
743
1264
|
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Creates an event whose {@code originalEvent} is {@code from}, with an optional `type` and `target` override.
|
|
1267
|
+
*
|
|
1268
|
+
* The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}.
|
|
1269
|
+
* @param from - The {@code originalEvent} for the returned event.
|
|
1270
|
+
* @param [type=from.type] - The type of the returned event.
|
|
1271
|
+
* @param target - The target of the returned event.
|
|
1272
|
+
*/
|
|
744
1273
|
createPointerEvent(from, type, target) {
|
|
745
1274
|
var _a;
|
|
746
1275
|
const event = this.allocateEvent(FederatedPointerEvent);
|
|
@@ -755,6 +1284,12 @@ class EventBoundary {
|
|
|
755
1284
|
}
|
|
756
1285
|
return event;
|
|
757
1286
|
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Creates a wheel event whose {@code originalEvent} is {@code from}.
|
|
1289
|
+
*
|
|
1290
|
+
* The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}.
|
|
1291
|
+
* @param from - The upstream wheel event.
|
|
1292
|
+
*/
|
|
758
1293
|
createWheelEvent(from) {
|
|
759
1294
|
const event = this.allocateEvent(FederatedWheelEvent);
|
|
760
1295
|
this.copyWheelData(from, event);
|
|
@@ -765,6 +1300,13 @@ class EventBoundary {
|
|
|
765
1300
|
event.target = this.hitTest(event.global.x, event.global.y);
|
|
766
1301
|
return event;
|
|
767
1302
|
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Clones the event {@code from}, with an optional {@code type} override.
|
|
1305
|
+
*
|
|
1306
|
+
* The event is allocated using {@link EventBoundary#allocateEvent this.allocateEvent}.
|
|
1307
|
+
* @param from - The event to clone.
|
|
1308
|
+
* @param [type=from.type] - The type of the returned event.
|
|
1309
|
+
*/
|
|
768
1310
|
clonePointerEvent(from, type) {
|
|
769
1311
|
const event = this.allocateEvent(FederatedPointerEvent);
|
|
770
1312
|
event.nativeEvent = from.nativeEvent;
|
|
@@ -772,17 +1314,45 @@ class EventBoundary {
|
|
|
772
1314
|
this.copyPointerData(from, event);
|
|
773
1315
|
this.copyMouseData(from, event);
|
|
774
1316
|
this.copyData(from, event);
|
|
1317
|
+
// copy propagation path for perf
|
|
775
1318
|
event.target = from.target;
|
|
776
1319
|
event.path = from.composedPath().slice();
|
|
777
1320
|
event.type = type !== null && type !== void 0 ? type : event.type;
|
|
778
1321
|
return event;
|
|
779
1322
|
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Copies wheel {@link FederatedWheelEvent} data from {@code from} into {@code to}.
|
|
1325
|
+
*
|
|
1326
|
+
* The following properties are copied:
|
|
1327
|
+
* + deltaMode
|
|
1328
|
+
* + deltaX
|
|
1329
|
+
* + deltaY
|
|
1330
|
+
* + deltaZ
|
|
1331
|
+
* @param from - The event to copy data from.
|
|
1332
|
+
* @param to - The event to copy data into.
|
|
1333
|
+
*/
|
|
780
1334
|
copyWheelData(from, to) {
|
|
781
1335
|
to.deltaMode = from.deltaMode;
|
|
782
1336
|
to.deltaX = from.deltaX;
|
|
783
1337
|
to.deltaY = from.deltaY;
|
|
784
1338
|
to.deltaZ = from.deltaZ;
|
|
785
1339
|
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Copies pointer {@link FederatedPointerEvent} data from {@code from} into {@code to}.
|
|
1342
|
+
*
|
|
1343
|
+
* The following properties are copied:
|
|
1344
|
+
* + pointerId
|
|
1345
|
+
* + width
|
|
1346
|
+
* + height
|
|
1347
|
+
* + isPrimary
|
|
1348
|
+
* + pointerType
|
|
1349
|
+
* + pressure
|
|
1350
|
+
* + tangentialPressure
|
|
1351
|
+
* + tiltX
|
|
1352
|
+
* + tiltY
|
|
1353
|
+
* @param from - The event to copy data from.
|
|
1354
|
+
* @param to - The event to copy data into.
|
|
1355
|
+
*/
|
|
786
1356
|
copyPointerData(from, to) {
|
|
787
1357
|
if (!(from instanceof FederatedPointerEvent && to instanceof FederatedPointerEvent))
|
|
788
1358
|
return;
|
|
@@ -797,6 +1367,28 @@ class EventBoundary {
|
|
|
797
1367
|
to.tiltY = from.tiltY;
|
|
798
1368
|
to.twist = from.twist;
|
|
799
1369
|
}
|
|
1370
|
+
/**
|
|
1371
|
+
* Copies mouse {@link FederatedMouseEvent} data from {@code from} to {@code to}.
|
|
1372
|
+
*
|
|
1373
|
+
* The following properties are copied:
|
|
1374
|
+
* + altKey
|
|
1375
|
+
* + button
|
|
1376
|
+
* + buttons
|
|
1377
|
+
* + clientX
|
|
1378
|
+
* + clientY
|
|
1379
|
+
* + metaKey
|
|
1380
|
+
* + movementX
|
|
1381
|
+
* + movementY
|
|
1382
|
+
* + pageX
|
|
1383
|
+
* + pageY
|
|
1384
|
+
* + x
|
|
1385
|
+
* + y
|
|
1386
|
+
* + screen
|
|
1387
|
+
* + shiftKey
|
|
1388
|
+
* + global
|
|
1389
|
+
* @param from - The event to copy data from.
|
|
1390
|
+
* @param to - The event to copy data into.
|
|
1391
|
+
*/
|
|
800
1392
|
copyMouseData(from, to) {
|
|
801
1393
|
if (!(from instanceof FederatedMouseEvent && to instanceof FederatedMouseEvent))
|
|
802
1394
|
return;
|
|
@@ -811,6 +1403,17 @@ class EventBoundary {
|
|
|
811
1403
|
to.shiftKey = from.shiftKey;
|
|
812
1404
|
to.global.copyFrom(from.global);
|
|
813
1405
|
}
|
|
1406
|
+
/**
|
|
1407
|
+
* Copies base {@link FederatedEvent} data from {@code from} into {@code to}.
|
|
1408
|
+
*
|
|
1409
|
+
* The following properties are copied:
|
|
1410
|
+
* + isTrusted
|
|
1411
|
+
* + srcElement
|
|
1412
|
+
* + timeStamp
|
|
1413
|
+
* + type
|
|
1414
|
+
* @param from - The event to copy data from.
|
|
1415
|
+
* @param to - The event to copy data into.
|
|
1416
|
+
*/
|
|
814
1417
|
copyData(from, to) {
|
|
815
1418
|
to.isTrusted = from.isTrusted;
|
|
816
1419
|
to.srcElement = from.srcElement;
|
|
@@ -822,6 +1425,11 @@ class EventBoundary {
|
|
|
822
1425
|
to.layer.copyFrom(from.layer);
|
|
823
1426
|
to.page.copyFrom(from.page);
|
|
824
1427
|
}
|
|
1428
|
+
/**
|
|
1429
|
+
* @param id - The pointer ID.
|
|
1430
|
+
* @returns The tracking data stored for the given pointer. If no data exists, a blank
|
|
1431
|
+
* state will be created.
|
|
1432
|
+
*/
|
|
825
1433
|
trackingData(id) {
|
|
826
1434
|
if (!this.mappingState.trackingData[id]) {
|
|
827
1435
|
this.mappingState.trackingData[id] = {
|
|
@@ -832,6 +1440,13 @@ class EventBoundary {
|
|
|
832
1440
|
}
|
|
833
1441
|
return this.mappingState.trackingData[id];
|
|
834
1442
|
}
|
|
1443
|
+
/**
|
|
1444
|
+
* Allocate a specific type of event from {@link EventBoundary#eventPool this.eventPool}.
|
|
1445
|
+
*
|
|
1446
|
+
* This allocation is constructor-agnostic, as long as it only takes one argument - this event
|
|
1447
|
+
* boundary.
|
|
1448
|
+
* @param constructor - The event's constructor.
|
|
1449
|
+
*/
|
|
835
1450
|
allocateEvent(constructor) {
|
|
836
1451
|
if (!this.eventPool.has(constructor)) {
|
|
837
1452
|
this.eventPool.set(constructor, []);
|
|
@@ -844,6 +1459,17 @@ class EventBoundary {
|
|
|
844
1459
|
event.target = null;
|
|
845
1460
|
return event;
|
|
846
1461
|
}
|
|
1462
|
+
/**
|
|
1463
|
+
* Frees the event and puts it back into the event pool.
|
|
1464
|
+
*
|
|
1465
|
+
* It is illegal to reuse the event until it is allocated again, using `this.allocateEvent`.
|
|
1466
|
+
*
|
|
1467
|
+
* It is also advised that events not allocated from {@link EventBoundary#allocateEvent this.allocateEvent}
|
|
1468
|
+
* not be freed. This is because of the possibility that the same event is freed twice, which can cause
|
|
1469
|
+
* it to be allocated twice & result in overwriting.
|
|
1470
|
+
* @param event - The event to be freed.
|
|
1471
|
+
* @throws Error if the event is managed by another event boundary.
|
|
1472
|
+
*/
|
|
847
1473
|
freeEvent(event) {
|
|
848
1474
|
if (event.manager !== this)
|
|
849
1475
|
throw new Error('It is illegal to free an event not managed by this EventBoundary!');
|
|
@@ -853,6 +1479,12 @@ class EventBoundary {
|
|
|
853
1479
|
}
|
|
854
1480
|
this.eventPool.get(constructor).push(event);
|
|
855
1481
|
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Similar to {@link EventEmitter.emit}, except it stops if the `propagationImmediatelyStopped` flag
|
|
1484
|
+
* is set on the event.
|
|
1485
|
+
* @param e - The event to call each listener with.
|
|
1486
|
+
* @param type - The event key.
|
|
1487
|
+
*/
|
|
856
1488
|
_notifyListeners(e, type) {
|
|
857
1489
|
const listeners = e.currentTarget._events[type];
|
|
858
1490
|
if (!listeners)
|
|
@@ -879,11 +1511,47 @@ const TOUCH_TO_POINTER = {
|
|
|
879
1511
|
touchmove: 'pointermove',
|
|
880
1512
|
touchcancel: 'pointercancel',
|
|
881
1513
|
};
|
|
1514
|
+
/**
|
|
1515
|
+
* 事件系统类
|
|
1516
|
+
*
|
|
1517
|
+
* EventSystem 负责处理所有 UI 交互事件(鼠标、触摸、指针等)。
|
|
1518
|
+
* 它将原生 DOM 事件转换为统一的 FederatedEvent,
|
|
1519
|
+
* 并通过事件边界(EventBoundary)将事件传播到场景图中的对象。
|
|
1520
|
+
*
|
|
1521
|
+
* 支持的事件类型:
|
|
1522
|
+
* - 指针事件:pointerdown、pointermove、pointerup 等
|
|
1523
|
+
* - 鼠标事件:mousedown、mousemove、mouseup 等
|
|
1524
|
+
* - 触摸事件:touchstart、touchmove、touchend 等
|
|
1525
|
+
* - 滚轮事件:wheel
|
|
1526
|
+
*
|
|
1527
|
+
* @example
|
|
1528
|
+
* ```typescript
|
|
1529
|
+
* const eventSystem = new EventSystem(renderer);
|
|
1530
|
+
* eventSystem.init({
|
|
1531
|
+
* eventMode: 'passive',
|
|
1532
|
+
* eventFeatures: {
|
|
1533
|
+
* move: true,
|
|
1534
|
+
* click: true,
|
|
1535
|
+
* wheel: true
|
|
1536
|
+
* }
|
|
1537
|
+
* });
|
|
1538
|
+
* ```
|
|
1539
|
+
*/
|
|
882
1540
|
class EventSystem {
|
|
1541
|
+
/**
|
|
1542
|
+
* @param {Renderer} renderer
|
|
1543
|
+
*/
|
|
883
1544
|
constructor(renderer) {
|
|
1545
|
+
/** Does the device support touch events https://www.w3.org/TR/touch-events/ */
|
|
884
1546
|
this.supportsTouchEvents = 'ontouchstart' in globalThis;
|
|
1547
|
+
/** Does the device support pointer events https://www.w3.org/Submission/pointer-events/ */
|
|
885
1548
|
this.supportsPointerEvents = !!globalThis.PointerEvent;
|
|
1549
|
+
/**
|
|
1550
|
+
* The DOM element to which the root event listeners are bound. This is automatically set to
|
|
1551
|
+
* the renderer's {@link Renderer#view view}.
|
|
1552
|
+
*/
|
|
886
1553
|
this.domElement = null;
|
|
1554
|
+
/** The resolution used to convert between the DOM client space into world space. */
|
|
887
1555
|
this.resolution = 1;
|
|
888
1556
|
this.renderer = renderer;
|
|
889
1557
|
this.rootBoundary = new EventBoundary(null);
|
|
@@ -911,9 +1579,20 @@ class EventSystem {
|
|
|
911
1579
|
this._onPointerOverOut = this._onPointerOverOut.bind(this);
|
|
912
1580
|
this.onWheel = this.onWheel.bind(this);
|
|
913
1581
|
}
|
|
1582
|
+
/**
|
|
1583
|
+
* The default interaction mode for all display objects.
|
|
1584
|
+
* @see Container.eventMode
|
|
1585
|
+
* @type {EventMode}
|
|
1586
|
+
* @readonly
|
|
1587
|
+
* @since 7.2.0
|
|
1588
|
+
*/
|
|
914
1589
|
static get defaultEventMode() {
|
|
915
1590
|
return this._defaultEventMode;
|
|
916
1591
|
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Runner init called, view is available at this point.
|
|
1594
|
+
* @ignore
|
|
1595
|
+
*/
|
|
917
1596
|
init(options) {
|
|
918
1597
|
var _a, _b;
|
|
919
1598
|
const { canvas, resolution } = this.renderer;
|
|
@@ -923,38 +1602,55 @@ class EventSystem {
|
|
|
923
1602
|
Object.assign(this.features, (_b = options.eventFeatures) !== null && _b !== void 0 ? _b : {});
|
|
924
1603
|
this.rootBoundary.enableGlobalMoveEvents = this.features.globalMove;
|
|
925
1604
|
}
|
|
1605
|
+
/**
|
|
1606
|
+
* Handle changing resolution.
|
|
1607
|
+
* @ignore
|
|
1608
|
+
*/
|
|
926
1609
|
resolutionChange(resolution) {
|
|
927
1610
|
this.resolution = resolution;
|
|
928
1611
|
}
|
|
1612
|
+
/** Destroys all event listeners and detaches the renderer. */
|
|
929
1613
|
destroy() {
|
|
930
1614
|
this.setTargetElement(null);
|
|
931
1615
|
this.renderer = null;
|
|
932
1616
|
this._currentCursor = null;
|
|
933
1617
|
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Sets the current cursor mode, handling any callbacks or CSS style changes.
|
|
1620
|
+
* @param mode - cursor mode, a key from the cursorStyles dictionary
|
|
1621
|
+
*/
|
|
934
1622
|
setCursor(mode) {
|
|
935
1623
|
if (!mode) {
|
|
936
1624
|
mode = 'default';
|
|
937
1625
|
}
|
|
938
1626
|
let applyStyles = true;
|
|
1627
|
+
// offscreen canvas does not support setting styles, but cursor modes can be functions,
|
|
1628
|
+
// in order to handle pixi rendered cursors, so we can't bail
|
|
939
1629
|
if (globalThis.OffscreenCanvas && this.domElement instanceof OffscreenCanvas) {
|
|
940
1630
|
applyStyles = false;
|
|
941
1631
|
}
|
|
1632
|
+
// if the mode didn't actually change, bail early
|
|
942
1633
|
if (this._currentCursor === mode) {
|
|
943
1634
|
return;
|
|
944
1635
|
}
|
|
945
1636
|
this._currentCursor = mode;
|
|
946
1637
|
const style = this.cursorStyles[mode];
|
|
1638
|
+
// only do things if there is a cursor style for it
|
|
947
1639
|
if (style) {
|
|
948
1640
|
switch (typeof style) {
|
|
949
1641
|
case 'string':
|
|
1642
|
+
// string styles are handled as cursor CSS
|
|
950
1643
|
if (applyStyles) {
|
|
951
1644
|
this.domElement.style.cursor = style;
|
|
952
1645
|
}
|
|
953
1646
|
break;
|
|
954
1647
|
case 'function':
|
|
1648
|
+
// functions are just called, and passed the cursor mode
|
|
955
1649
|
style(mode);
|
|
956
1650
|
break;
|
|
957
1651
|
case 'object':
|
|
1652
|
+
// if it is an object, assume that it is a dictionary of CSS styles,
|
|
1653
|
+
// apply it to the interactionDOMElement
|
|
958
1654
|
if (applyStyles) {
|
|
959
1655
|
Object.assign(this.domElement.style, style);
|
|
960
1656
|
}
|
|
@@ -964,17 +1660,34 @@ class EventSystem {
|
|
|
964
1660
|
else if (applyStyles &&
|
|
965
1661
|
typeof mode === 'string' &&
|
|
966
1662
|
!Object.prototype.hasOwnProperty.call(this.cursorStyles, mode)) {
|
|
1663
|
+
// if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
|
|
1664
|
+
// for the mode, then assume that the dev wants it to be CSS for the cursor.
|
|
967
1665
|
this.domElement.style.cursor = mode;
|
|
968
1666
|
}
|
|
969
1667
|
}
|
|
1668
|
+
/**
|
|
1669
|
+
* The global pointer event.
|
|
1670
|
+
* Useful for getting the pointer position without listening to events.
|
|
1671
|
+
* @since 7.2.0
|
|
1672
|
+
*/
|
|
970
1673
|
get pointer() {
|
|
971
1674
|
return this._rootPointerEvent;
|
|
972
1675
|
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Event handler for pointer down events on {@link EventSystem#domElement this.domElement}.
|
|
1678
|
+
* @param nativeEvent - The native mouse/pointer/touch event.
|
|
1679
|
+
*/
|
|
973
1680
|
_onPointerDown(nativeEvent) {
|
|
974
1681
|
if (!this.features.click)
|
|
975
1682
|
return;
|
|
976
1683
|
this.rootBoundary.rootTarget = this.renderer.lastObjectRendered;
|
|
977
1684
|
const events = this._normalizeToPointerData(nativeEvent);
|
|
1685
|
+
/*
|
|
1686
|
+
* No need to prevent default on natural pointer events, as there are no side effects
|
|
1687
|
+
* Normalized events, however, may have the double mousedown/touchstart issue on the native android browser,
|
|
1688
|
+
* so still need to be prevented.
|
|
1689
|
+
*/
|
|
1690
|
+
// Guaranteed that there will be at least one event in events, and all events must have the same pointer type
|
|
978
1691
|
if (this.autoPreventDefault && events[0].isNormalized) {
|
|
979
1692
|
const cancelable = nativeEvent.cancelable || !('cancelable' in nativeEvent);
|
|
980
1693
|
if (cancelable) {
|
|
@@ -989,6 +1702,10 @@ class EventSystem {
|
|
|
989
1702
|
}
|
|
990
1703
|
this.setCursor(this.rootBoundary.cursor);
|
|
991
1704
|
}
|
|
1705
|
+
/**
|
|
1706
|
+
* Event handler for pointer move events on on {@link EventSystem#domElement this.domElement}.
|
|
1707
|
+
* @param nativeEvent - The native mouse/pointer/touch events.
|
|
1708
|
+
*/
|
|
992
1709
|
_onPointerMove(nativeEvent) {
|
|
993
1710
|
if (!this.features.move)
|
|
994
1711
|
return;
|
|
@@ -1001,6 +1718,10 @@ class EventSystem {
|
|
|
1001
1718
|
}
|
|
1002
1719
|
this.setCursor(this.rootBoundary.cursor);
|
|
1003
1720
|
}
|
|
1721
|
+
/**
|
|
1722
|
+
* Event handler for pointer up events on {@link EventSystem#domElement this.domElement}.
|
|
1723
|
+
* @param nativeEvent - The native mouse/pointer/touch event.
|
|
1724
|
+
*/
|
|
1004
1725
|
_onPointerUp(nativeEvent) {
|
|
1005
1726
|
if (!this.features.click)
|
|
1006
1727
|
return;
|
|
@@ -1014,6 +1735,10 @@ class EventSystem {
|
|
|
1014
1735
|
}
|
|
1015
1736
|
this.setCursor(this.rootBoundary.cursor);
|
|
1016
1737
|
}
|
|
1738
|
+
/**
|
|
1739
|
+
* Event handler for pointer over & out events on {@link EventSystem#domElement this.domElement}.
|
|
1740
|
+
* @param nativeEvent - The native mouse/pointer/touch event.
|
|
1741
|
+
*/
|
|
1017
1742
|
_onPointerOverOut(nativeEvent) {
|
|
1018
1743
|
if (!this.features.click)
|
|
1019
1744
|
return;
|
|
@@ -1025,6 +1750,10 @@ class EventSystem {
|
|
|
1025
1750
|
}
|
|
1026
1751
|
this.setCursor(this.rootBoundary.cursor);
|
|
1027
1752
|
}
|
|
1753
|
+
/**
|
|
1754
|
+
* Passive handler for `wheel` events on {@link EventSystem.domElement this.domElement}.
|
|
1755
|
+
* @param nativeEvent - The native wheel event.
|
|
1756
|
+
*/
|
|
1028
1757
|
onWheel(nativeEvent) {
|
|
1029
1758
|
if (!this.features.wheel)
|
|
1030
1759
|
return;
|
|
@@ -1032,12 +1761,19 @@ class EventSystem {
|
|
|
1032
1761
|
this.rootBoundary.rootTarget = this.renderer.lastObjectRendered;
|
|
1033
1762
|
this.rootBoundary.mapEvent(wheelEvent);
|
|
1034
1763
|
}
|
|
1764
|
+
/**
|
|
1765
|
+
* Sets the {@link EventSystem#domElement domElement} and binds event listeners.
|
|
1766
|
+
*
|
|
1767
|
+
* To deregister the current DOM element without setting a new one, pass {@code null}.
|
|
1768
|
+
* @param element - The new DOM element.
|
|
1769
|
+
*/
|
|
1035
1770
|
setTargetElement(element) {
|
|
1036
1771
|
this._removeEvents();
|
|
1037
1772
|
this.domElement = element;
|
|
1038
1773
|
EventsTicker.domElement = element;
|
|
1039
1774
|
this._addEvents();
|
|
1040
1775
|
}
|
|
1776
|
+
/** Register event listeners on {@link Renderer#domElement this.domElement}. */
|
|
1041
1777
|
_addEvents() {
|
|
1042
1778
|
if (this._eventsAdded || !this.domElement) {
|
|
1043
1779
|
return;
|
|
@@ -1060,6 +1796,10 @@ class EventSystem {
|
|
|
1060
1796
|
id = key;
|
|
1061
1797
|
}
|
|
1062
1798
|
}
|
|
1799
|
+
/*
|
|
1800
|
+
* These events are added first, so that if pointer events are normalized, they are fired
|
|
1801
|
+
* in the same order as non-normalized events. ie. pointer event 1st, mouse / touch 2nd
|
|
1802
|
+
*/
|
|
1063
1803
|
EventSystem.eventsHandler[id] = {
|
|
1064
1804
|
pointermove: this._onPointerMove.bind(this),
|
|
1065
1805
|
pointerdown: this._onPointerDown.bind(this),
|
|
@@ -1078,12 +1818,14 @@ class EventSystem {
|
|
|
1078
1818
|
EventSystem.eventsHandler[id]['wheel'] = this.onWheel.bind(this);
|
|
1079
1819
|
this._eventsAdded = true;
|
|
1080
1820
|
}
|
|
1821
|
+
/** Unregister event listeners on {@link EventSystem#domElement this.domElement}. */
|
|
1081
1822
|
_removeEvents() {
|
|
1082
1823
|
if (!this._eventsAdded || !this.domElement) {
|
|
1083
1824
|
return;
|
|
1084
1825
|
}
|
|
1085
1826
|
EventsTicker.removeTickerListener();
|
|
1086
1827
|
const style = this.domElement.style;
|
|
1828
|
+
// offscreen canvas does not have style, so check first
|
|
1087
1829
|
if (style) {
|
|
1088
1830
|
if (globalThis.navigator.msPointerEnabled) {
|
|
1089
1831
|
style.msContentZooming = '';
|
|
@@ -1096,6 +1838,14 @@ class EventSystem {
|
|
|
1096
1838
|
this.domElement = null;
|
|
1097
1839
|
this._eventsAdded = false;
|
|
1098
1840
|
}
|
|
1841
|
+
/**
|
|
1842
|
+
* Maps x and y coords from a DOM object and maps them correctly to the PixiJS view. The
|
|
1843
|
+
* resulting value is stored in the point. This takes into account the fact that the DOM
|
|
1844
|
+
* element could be scaled and positioned anywhere on the screen.
|
|
1845
|
+
* @param {PointData} point - the point that the result will be stored in
|
|
1846
|
+
* @param {number} x - the x coord of the position to map
|
|
1847
|
+
* @param {number} y - the y coord of the position to map
|
|
1848
|
+
*/
|
|
1099
1849
|
mapPositionToPoint(point, x, y, e) {
|
|
1100
1850
|
const resolutionMultiplier = 1.0 / this.resolution;
|
|
1101
1851
|
const rect = e.canvasRect || {
|
|
@@ -1110,12 +1860,34 @@ class EventSystem {
|
|
|
1110
1860
|
point.x = (x - rect.left) * (domElement.width / rect.width) * resolutionMultiplier;
|
|
1111
1861
|
point.y = (y - rect.top) * (domElement.height / rect.height) * resolutionMultiplier;
|
|
1112
1862
|
}
|
|
1863
|
+
/**
|
|
1864
|
+
* Ensures that the original event object contains all data that a regular pointer event would have
|
|
1865
|
+
* @param event - The original event data from a touch or mouse event
|
|
1866
|
+
* @returns An array containing a single normalized pointer event, in the case of a pointer
|
|
1867
|
+
* or mouse event, or a multiple normalized pointer events if there are multiple changed touches
|
|
1868
|
+
*/
|
|
1113
1869
|
_normalizeToPointerData(event) {
|
|
1870
|
+
// @ts-ignore
|
|
1114
1871
|
return event.normalizedEvents;
|
|
1115
1872
|
}
|
|
1873
|
+
/**
|
|
1874
|
+
* Normalizes the native {@link https://w3c.github.io/uievents/#interface-wheelevent WheelEvent}.
|
|
1875
|
+
*
|
|
1876
|
+
* The returned {@link FederatedWheelEvent} is a shared instance. It will not persist across
|
|
1877
|
+
* multiple native wheel events.
|
|
1878
|
+
* @param nativeEvent - The native wheel event that occurred on the canvas.
|
|
1879
|
+
* @returns A federated wheel event.
|
|
1880
|
+
*/
|
|
1116
1881
|
normalizeWheelEvent(nativeEvent) {
|
|
1117
1882
|
const event = this._rootWheelEvent;
|
|
1118
1883
|
this._transferMouseData(event, nativeEvent);
|
|
1884
|
+
// When WheelEvent is triggered by scrolling with mouse wheel, reading WheelEvent.deltaMode
|
|
1885
|
+
// before deltaX/deltaY/deltaZ on Firefox will result in WheelEvent.DOM_DELTA_LINE (1),
|
|
1886
|
+
// while reading WheelEvent.deltaMode after deltaX/deltaY/deltaZ on Firefox or reading
|
|
1887
|
+
// in any order on other browsers will result in WheelEvent.DOM_DELTA_PIXEL (0).
|
|
1888
|
+
// Therefore, we need to read WheelEvent.deltaMode after deltaX/deltaY/deltaZ in order to
|
|
1889
|
+
// make its behavior more consistent across browsers.
|
|
1890
|
+
// @see https://github.com/pixijs/pixijs/issues/8970
|
|
1119
1891
|
event.deltaX = nativeEvent.deltaX;
|
|
1120
1892
|
event.deltaY = nativeEvent.deltaY;
|
|
1121
1893
|
event.deltaZ = nativeEvent.deltaZ;
|
|
@@ -1127,6 +1899,11 @@ class EventSystem {
|
|
|
1127
1899
|
event.type = nativeEvent.type;
|
|
1128
1900
|
return event;
|
|
1129
1901
|
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Normalizes the `nativeEvent` into a federateed {@link FederatedPointerEvent}.
|
|
1904
|
+
* @param event
|
|
1905
|
+
* @param nativeEvent
|
|
1906
|
+
*/
|
|
1130
1907
|
_bootstrapEvent(event, nativeEvent, nEvent) {
|
|
1131
1908
|
event.originalEvent = null;
|
|
1132
1909
|
event.nativeEvent = nativeEvent;
|
|
@@ -1142,8 +1919,8 @@ class EventSystem {
|
|
|
1142
1919
|
event.twist = nativeEvent.twist;
|
|
1143
1920
|
this._transferMouseData(event, nativeEvent);
|
|
1144
1921
|
this.mapPositionToPoint(event.screen, nativeEvent.clientX, nativeEvent.clientY, nEvent);
|
|
1145
|
-
event.global.copyFrom(event.screen);
|
|
1146
|
-
event.offset.copyFrom(event.screen);
|
|
1922
|
+
event.global.copyFrom(event.screen); // global = screen for top-level
|
|
1923
|
+
event.offset.copyFrom(event.screen); // EventBoundary recalculates using its rootTarget
|
|
1147
1924
|
event.isTrusted = nativeEvent.isTrusted;
|
|
1148
1925
|
if (event.type === 'pointerleave') {
|
|
1149
1926
|
event.type = 'pointerout';
|
|
@@ -1156,6 +1933,11 @@ class EventSystem {
|
|
|
1156
1933
|
}
|
|
1157
1934
|
return event;
|
|
1158
1935
|
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Transfers base & mouse event data from the {@code nativeEvent} to the federated event.
|
|
1938
|
+
* @param event
|
|
1939
|
+
* @param nativeEvent
|
|
1940
|
+
*/
|
|
1159
1941
|
_transferMouseData(event, nativeEvent) {
|
|
1160
1942
|
event.isTrusted = nativeEvent.isTrusted;
|
|
1161
1943
|
event.srcElement = nativeEvent.srcElement;
|
|
@@ -1176,61 +1958,397 @@ class EventSystem {
|
|
|
1176
1958
|
event.shiftKey = nativeEvent.shiftKey;
|
|
1177
1959
|
}
|
|
1178
1960
|
}
|
|
1961
|
+
/** @ignore */
|
|
1179
1962
|
EventSystem.extension = {
|
|
1180
1963
|
name: 'events',
|
|
1181
1964
|
type: [ExtensionType.WebGLSystem, ExtensionType.CanvasSystem, ExtensionType.WebGPUSystem],
|
|
1182
1965
|
priority: -1,
|
|
1183
1966
|
};
|
|
1967
|
+
/** 画布映射表 */
|
|
1184
1968
|
EventSystem.canvasMap = {};
|
|
1969
|
+
/** 事件处理器映射表 */
|
|
1185
1970
|
EventSystem.eventsHandler = {};
|
|
1971
|
+
/**
|
|
1972
|
+
* 事件系统的默认功能配置
|
|
1973
|
+
* @since 7.2.0
|
|
1974
|
+
*/
|
|
1186
1975
|
EventSystem.defaultEventFeatures = {
|
|
1976
|
+
/** 启用指针移动相关事件 */
|
|
1187
1977
|
move: true,
|
|
1978
|
+
/** 启用全局指针移动事件 */
|
|
1188
1979
|
globalMove: true,
|
|
1980
|
+
/** 启用点击相关事件 */
|
|
1189
1981
|
click: true,
|
|
1982
|
+
/** 启用滚轮事件 */
|
|
1190
1983
|
wheel: true,
|
|
1191
1984
|
};
|
|
1192
1985
|
|
|
1193
1986
|
const FederatedContainer = {
|
|
1987
|
+
/**
|
|
1988
|
+
* Property-based event handler for the `click` event.
|
|
1989
|
+
* @memberof scene.Container#
|
|
1990
|
+
* @default null
|
|
1991
|
+
* @example
|
|
1992
|
+
* this.onclick = (event) => {
|
|
1993
|
+
* //some function here that happens on click
|
|
1994
|
+
* }
|
|
1995
|
+
*/
|
|
1194
1996
|
onclick: null,
|
|
1997
|
+
/**
|
|
1998
|
+
* Property-based event handler for the `mousedown` event.
|
|
1999
|
+
* @memberof scene.Container#
|
|
2000
|
+
* @default null
|
|
2001
|
+
* @example
|
|
2002
|
+
* this.onmousedown = (event) => {
|
|
2003
|
+
* //some function here that happens on mousedown
|
|
2004
|
+
* }
|
|
2005
|
+
*/
|
|
1195
2006
|
onmousedown: null,
|
|
2007
|
+
/**
|
|
2008
|
+
* Property-based event handler for the `mouseenter` event.
|
|
2009
|
+
* @memberof scene.Container#
|
|
2010
|
+
* @default null
|
|
2011
|
+
* @example
|
|
2012
|
+
* this.onmouseenter = (event) => {
|
|
2013
|
+
* //some function here that happens on mouseenter
|
|
2014
|
+
* }
|
|
2015
|
+
*/
|
|
1196
2016
|
onmouseenter: null,
|
|
2017
|
+
/**
|
|
2018
|
+
* Property-based event handler for the `mouseleave` event.
|
|
2019
|
+
* @memberof scene.Container#
|
|
2020
|
+
* @default null
|
|
2021
|
+
* @example
|
|
2022
|
+
* this.onmouseleave = (event) => {
|
|
2023
|
+
* //some function here that happens on mouseleave
|
|
2024
|
+
* }
|
|
2025
|
+
*/
|
|
1197
2026
|
onmouseleave: null,
|
|
2027
|
+
/**
|
|
2028
|
+
* Property-based event handler for the `mousemove` event.
|
|
2029
|
+
* @memberof scene.Container#
|
|
2030
|
+
* @default null
|
|
2031
|
+
* @example
|
|
2032
|
+
* this.onmousemove = (event) => {
|
|
2033
|
+
* //some function here that happens on mousemove
|
|
2034
|
+
* }
|
|
2035
|
+
*/
|
|
1198
2036
|
onmousemove: null,
|
|
2037
|
+
/**
|
|
2038
|
+
* Property-based event handler for the `globalmousemove` event.
|
|
2039
|
+
* @memberof scene.Container#
|
|
2040
|
+
* @default null
|
|
2041
|
+
* @example
|
|
2042
|
+
* this.onglobalmousemove = (event) => {
|
|
2043
|
+
* //some function here that happens on globalmousemove
|
|
2044
|
+
* }
|
|
2045
|
+
*/
|
|
1199
2046
|
onglobalmousemove: null,
|
|
2047
|
+
/**
|
|
2048
|
+
* Property-based event handler for the `mouseout` event.
|
|
2049
|
+
* @memberof scene.Container#
|
|
2050
|
+
* @default null
|
|
2051
|
+
* @example
|
|
2052
|
+
* this.onmouseout = (event) => {
|
|
2053
|
+
* //some function here that happens on mouseout
|
|
2054
|
+
* }
|
|
2055
|
+
*/
|
|
1200
2056
|
onmouseout: null,
|
|
2057
|
+
/**
|
|
2058
|
+
* Property-based event handler for the `mouseover` event.
|
|
2059
|
+
* @memberof scene.Container#
|
|
2060
|
+
* @default null
|
|
2061
|
+
* @example
|
|
2062
|
+
* this.onmouseover = (event) => {
|
|
2063
|
+
* //some function here that happens on mouseover
|
|
2064
|
+
* }
|
|
2065
|
+
*/
|
|
1201
2066
|
onmouseover: null,
|
|
2067
|
+
/**
|
|
2068
|
+
* Property-based event handler for the `mouseup` event.
|
|
2069
|
+
* @memberof scene.Container#
|
|
2070
|
+
* @default null
|
|
2071
|
+
* @example
|
|
2072
|
+
* this.onmouseup = (event) => {
|
|
2073
|
+
* //some function here that happens on mouseup
|
|
2074
|
+
* }
|
|
2075
|
+
*/
|
|
1202
2076
|
onmouseup: null,
|
|
2077
|
+
/**
|
|
2078
|
+
* Property-based event handler for the `mouseupoutside` event.
|
|
2079
|
+
* @memberof scene.Container#
|
|
2080
|
+
* @default null
|
|
2081
|
+
* @example
|
|
2082
|
+
* this.onmouseupoutside = (event) => {
|
|
2083
|
+
* //some function here that happens on mouseupoutside
|
|
2084
|
+
* }
|
|
2085
|
+
*/
|
|
1203
2086
|
onmouseupoutside: null,
|
|
2087
|
+
/**
|
|
2088
|
+
* Property-based event handler for the `pointercancel` event.
|
|
2089
|
+
* @memberof scene.Container#
|
|
2090
|
+
* @default null
|
|
2091
|
+
* @example
|
|
2092
|
+
* this.onpointercancel = (event) => {
|
|
2093
|
+
* //some function here that happens on pointercancel
|
|
2094
|
+
* }
|
|
2095
|
+
*/
|
|
1204
2096
|
onpointercancel: null,
|
|
2097
|
+
/**
|
|
2098
|
+
* Property-based event handler for the `pointerdown` event.
|
|
2099
|
+
* @memberof scene.Container#
|
|
2100
|
+
* @default null
|
|
2101
|
+
* @example
|
|
2102
|
+
* this.onpointerdown = (event) => {
|
|
2103
|
+
* //some function here that happens on pointerdown
|
|
2104
|
+
* }
|
|
2105
|
+
*/
|
|
1205
2106
|
onpointerdown: null,
|
|
2107
|
+
/**
|
|
2108
|
+
* Property-based event handler for the `pointerenter` event.
|
|
2109
|
+
* @memberof scene.Container#
|
|
2110
|
+
* @default null
|
|
2111
|
+
* @example
|
|
2112
|
+
* this.onpointerenter = (event) => {
|
|
2113
|
+
* //some function here that happens on pointerenter
|
|
2114
|
+
* }
|
|
2115
|
+
*/
|
|
1206
2116
|
onpointerenter: null,
|
|
2117
|
+
/**
|
|
2118
|
+
* Property-based event handler for the `pointerleave` event.
|
|
2119
|
+
* @memberof scene.Container#
|
|
2120
|
+
* @default null
|
|
2121
|
+
* @example
|
|
2122
|
+
* this.onpointerleave = (event) => {
|
|
2123
|
+
* //some function here that happens on pointerleave
|
|
2124
|
+
* }
|
|
2125
|
+
*/
|
|
1207
2126
|
onpointerleave: null,
|
|
2127
|
+
/**
|
|
2128
|
+
* Property-based event handler for the `pointermove` event.
|
|
2129
|
+
* @memberof scene.Container#
|
|
2130
|
+
* @default null
|
|
2131
|
+
* @example
|
|
2132
|
+
* this.onpointermove = (event) => {
|
|
2133
|
+
* //some function here that happens on pointermove
|
|
2134
|
+
* }
|
|
2135
|
+
*/
|
|
1208
2136
|
onpointermove: null,
|
|
2137
|
+
/**
|
|
2138
|
+
* Property-based event handler for the `globalpointermove` event.
|
|
2139
|
+
* @memberof scene.Container#
|
|
2140
|
+
* @default null
|
|
2141
|
+
* @example
|
|
2142
|
+
* this.onglobalpointermove = (event) => {
|
|
2143
|
+
* //some function here that happens on globalpointermove
|
|
2144
|
+
* }
|
|
2145
|
+
*/
|
|
1209
2146
|
onglobalpointermove: null,
|
|
2147
|
+
/**
|
|
2148
|
+
* Property-based event handler for the `pointerout` event.
|
|
2149
|
+
* @memberof scene.Container#
|
|
2150
|
+
* @default null
|
|
2151
|
+
* @example
|
|
2152
|
+
* this.onpointerout = (event) => {
|
|
2153
|
+
* //some function here that happens on pointerout
|
|
2154
|
+
* }
|
|
2155
|
+
*/
|
|
1210
2156
|
onpointerout: null,
|
|
2157
|
+
/**
|
|
2158
|
+
* Property-based event handler for the `pointerover` event.
|
|
2159
|
+
* @memberof scene.Container#
|
|
2160
|
+
* @default null
|
|
2161
|
+
* @example
|
|
2162
|
+
* this.onpointerover = (event) => {
|
|
2163
|
+
* //some function here that happens on pointerover
|
|
2164
|
+
* }
|
|
2165
|
+
*/
|
|
1211
2166
|
onpointerover: null,
|
|
2167
|
+
/**
|
|
2168
|
+
* Property-based event handler for the `pointertap` event.
|
|
2169
|
+
* @memberof scene.Container#
|
|
2170
|
+
* @default null
|
|
2171
|
+
* @example
|
|
2172
|
+
* this.onpointertap = (event) => {
|
|
2173
|
+
* //some function here that happens on pointertap
|
|
2174
|
+
* }
|
|
2175
|
+
*/
|
|
1212
2176
|
onpointertap: null,
|
|
2177
|
+
/**
|
|
2178
|
+
* Property-based event handler for the `pointerup` event.
|
|
2179
|
+
* @memberof scene.Container#
|
|
2180
|
+
* @default null
|
|
2181
|
+
* @example
|
|
2182
|
+
* this.onpointerup = (event) => {
|
|
2183
|
+
* //some function here that happens on pointerup
|
|
2184
|
+
* }
|
|
2185
|
+
*/
|
|
1213
2186
|
onpointerup: null,
|
|
2187
|
+
/**
|
|
2188
|
+
* Property-based event handler for the `pointerupoutside` event.
|
|
2189
|
+
* @memberof scene.Container#
|
|
2190
|
+
* @default null
|
|
2191
|
+
* @example
|
|
2192
|
+
* this.onpointerupoutside = (event) => {
|
|
2193
|
+
* //some function here that happens on pointerupoutside
|
|
2194
|
+
* }
|
|
2195
|
+
*/
|
|
1214
2196
|
onpointerupoutside: null,
|
|
2197
|
+
/**
|
|
2198
|
+
* Property-based event handler for the `rightclick` event.
|
|
2199
|
+
* @memberof scene.Container#
|
|
2200
|
+
* @default null
|
|
2201
|
+
* @example
|
|
2202
|
+
* this.onrightclick = (event) => {
|
|
2203
|
+
* //some function here that happens on rightclick
|
|
2204
|
+
* }
|
|
2205
|
+
*/
|
|
1215
2206
|
onrightclick: null,
|
|
2207
|
+
/**
|
|
2208
|
+
* Property-based event handler for the `rightdown` event.
|
|
2209
|
+
* @memberof scene.Container#
|
|
2210
|
+
* @default null
|
|
2211
|
+
* @example
|
|
2212
|
+
* this.onrightdown = (event) => {
|
|
2213
|
+
* //some function here that happens on rightdown
|
|
2214
|
+
* }
|
|
2215
|
+
*/
|
|
1216
2216
|
onrightdown: null,
|
|
2217
|
+
/**
|
|
2218
|
+
* Property-based event handler for the `rightup` event.
|
|
2219
|
+
* @memberof scene.Container#
|
|
2220
|
+
* @default null
|
|
2221
|
+
* @example
|
|
2222
|
+
* this.onrightup = (event) => {
|
|
2223
|
+
* //some function here that happens on rightup
|
|
2224
|
+
* }
|
|
2225
|
+
*/
|
|
1217
2226
|
onrightup: null,
|
|
2227
|
+
/**
|
|
2228
|
+
* Property-based event handler for the `rightupoutside` event.
|
|
2229
|
+
* @memberof scene.Container#
|
|
2230
|
+
* @default null
|
|
2231
|
+
* @example
|
|
2232
|
+
* this.onrightupoutside = (event) => {
|
|
2233
|
+
* //some function here that happens on rightupoutside
|
|
2234
|
+
* }
|
|
2235
|
+
*/
|
|
1218
2236
|
onrightupoutside: null,
|
|
2237
|
+
/**
|
|
2238
|
+
* Property-based event handler for the `tap` event.
|
|
2239
|
+
* @memberof scene.Container#
|
|
2240
|
+
* @default null
|
|
2241
|
+
* @example
|
|
2242
|
+
* this.ontap = (event) => {
|
|
2243
|
+
* //some function here that happens on tap
|
|
2244
|
+
* }
|
|
2245
|
+
*/
|
|
1219
2246
|
ontap: null,
|
|
2247
|
+
/**
|
|
2248
|
+
* Property-based event handler for the `touchcancel` event.
|
|
2249
|
+
* @memberof scene.Container#
|
|
2250
|
+
* @default null
|
|
2251
|
+
* @example
|
|
2252
|
+
* this.ontouchcancel = (event) => {
|
|
2253
|
+
* //some function here that happens on touchcancel
|
|
2254
|
+
* }
|
|
2255
|
+
*/
|
|
1220
2256
|
ontouchcancel: null,
|
|
2257
|
+
/**
|
|
2258
|
+
* Property-based event handler for the `touchend` event.
|
|
2259
|
+
* @memberof scene.Container#
|
|
2260
|
+
* @default null
|
|
2261
|
+
* @example
|
|
2262
|
+
* this.ontouchend = (event) => {
|
|
2263
|
+
* //some function here that happens on touchend
|
|
2264
|
+
* }
|
|
2265
|
+
*/
|
|
1221
2266
|
ontouchend: null,
|
|
2267
|
+
/**
|
|
2268
|
+
* Property-based event handler for the `touchendoutside` event.
|
|
2269
|
+
* @memberof scene.Container#
|
|
2270
|
+
* @default null
|
|
2271
|
+
* @example
|
|
2272
|
+
* this.ontouchendoutside = (event) => {
|
|
2273
|
+
* //some function here that happens on touchendoutside
|
|
2274
|
+
* }
|
|
2275
|
+
*/
|
|
1222
2276
|
ontouchendoutside: null,
|
|
2277
|
+
/**
|
|
2278
|
+
* Property-based event handler for the `touchmove` event.
|
|
2279
|
+
* @memberof scene.Container#
|
|
2280
|
+
* @default null
|
|
2281
|
+
* @example
|
|
2282
|
+
* this.ontouchmove = (event) => {
|
|
2283
|
+
* //some function here that happens on touchmove
|
|
2284
|
+
* }
|
|
2285
|
+
*/
|
|
1223
2286
|
ontouchmove: null,
|
|
2287
|
+
/**
|
|
2288
|
+
* Property-based event handler for the `globaltouchmove` event.
|
|
2289
|
+
* @memberof scene.Container#
|
|
2290
|
+
* @default null
|
|
2291
|
+
* @example
|
|
2292
|
+
* this.onglobaltouchmove = (event) => {
|
|
2293
|
+
* //some function here that happens on globaltouchmove
|
|
2294
|
+
* }
|
|
2295
|
+
*/
|
|
1224
2296
|
onglobaltouchmove: null,
|
|
2297
|
+
/**
|
|
2298
|
+
* Property-based event handler for the `touchstart` event.
|
|
2299
|
+
* @memberof scene.Container#
|
|
2300
|
+
* @default null
|
|
2301
|
+
* @example
|
|
2302
|
+
* this.ontouchstart = (event) => {
|
|
2303
|
+
* //some function here that happens on touchstart
|
|
2304
|
+
* }
|
|
2305
|
+
*/
|
|
1225
2306
|
ontouchstart: null,
|
|
2307
|
+
/**
|
|
2308
|
+
* Property-based event handler for the `wheel` event.
|
|
2309
|
+
* @memberof scene.Container#
|
|
2310
|
+
* @default null
|
|
2311
|
+
* @example
|
|
2312
|
+
* this.onwheel = (event) => {
|
|
2313
|
+
* //some function here that happens on wheel
|
|
2314
|
+
* }
|
|
2315
|
+
*/
|
|
1226
2316
|
onwheel: null,
|
|
2317
|
+
/**
|
|
2318
|
+
* Enable interaction events for the Container. Touch, pointer and mouse
|
|
2319
|
+
* @memberof scene.Container#
|
|
2320
|
+
*/
|
|
1227
2321
|
get interactive() {
|
|
1228
2322
|
return this.eventMode === 'dynamic' || this.eventMode === 'static';
|
|
1229
2323
|
},
|
|
1230
2324
|
set interactive(value) {
|
|
1231
2325
|
this.eventMode = value ? 'static' : 'passive';
|
|
1232
2326
|
},
|
|
2327
|
+
/**
|
|
2328
|
+
* @ignore
|
|
2329
|
+
*/
|
|
1233
2330
|
_internalEventMode: undefined,
|
|
2331
|
+
/**
|
|
2332
|
+
* Enable interaction events for the Container. Touch, pointer and mouse.
|
|
2333
|
+
* There are 5 types of interaction settings:
|
|
2334
|
+
* - `'none'`: Ignores all interaction events, even on its children.
|
|
2335
|
+
* - `'passive'`: **(default)** Does not emit events and ignores all hit testing on itself and non-interactive children.
|
|
2336
|
+
* Interactive children will still emit events.
|
|
2337
|
+
* - `'auto'`: Does not emit events but is hit tested if parent is interactive. Same as `interactive = false` in v7
|
|
2338
|
+
* - `'static'`: Emit events and is hit tested. Same as `interaction = true` in v7
|
|
2339
|
+
* - `'dynamic'`: Emits events and is hit tested but will also receive mock interaction events fired from a ticker to
|
|
2340
|
+
* allow for interaction when the mouse isn't moving
|
|
2341
|
+
* @example
|
|
2342
|
+
* import { Sprite } from 'pixi.js';
|
|
2343
|
+
*
|
|
2344
|
+
* const sprite = new Sprite(texture);
|
|
2345
|
+
* sprite.eventMode = 'static';
|
|
2346
|
+
* sprite.on('tap', (event) => {
|
|
2347
|
+
* // Handle event
|
|
2348
|
+
* });
|
|
2349
|
+
* @memberof scene.Container#
|
|
2350
|
+
* @since 7.2.0
|
|
2351
|
+
*/
|
|
1234
2352
|
get eventMode() {
|
|
1235
2353
|
var _a;
|
|
1236
2354
|
return (_a = this._internalEventMode) !== null && _a !== void 0 ? _a : EventSystem.defaultEventMode;
|
|
@@ -1238,11 +2356,84 @@ const FederatedContainer = {
|
|
|
1238
2356
|
set eventMode(value) {
|
|
1239
2357
|
this._internalEventMode = value;
|
|
1240
2358
|
},
|
|
2359
|
+
/**
|
|
2360
|
+
* Determines if the container is interactive or not
|
|
2361
|
+
* @returns {boolean} Whether the container is interactive or not
|
|
2362
|
+
* @memberof scene.Container#
|
|
2363
|
+
* @since 7.2.0
|
|
2364
|
+
* @example
|
|
2365
|
+
* import { Sprite } from 'pixi.js';
|
|
2366
|
+
*
|
|
2367
|
+
* const sprite = new Sprite(texture);
|
|
2368
|
+
* sprite.eventMode = 'static';
|
|
2369
|
+
* sprite.isInteractive(); // true
|
|
2370
|
+
*
|
|
2371
|
+
* sprite.eventMode = 'dynamic';
|
|
2372
|
+
* sprite.isInteractive(); // true
|
|
2373
|
+
*
|
|
2374
|
+
* sprite.eventMode = 'none';
|
|
2375
|
+
* sprite.isInteractive(); // false
|
|
2376
|
+
*
|
|
2377
|
+
* sprite.eventMode = 'passive';
|
|
2378
|
+
* sprite.isInteractive(); // false
|
|
2379
|
+
*
|
|
2380
|
+
* sprite.eventMode = 'auto';
|
|
2381
|
+
* sprite.isInteractive(); // false
|
|
2382
|
+
*/
|
|
1241
2383
|
isInteractive() {
|
|
1242
2384
|
return this.eventMode === 'static' || this.eventMode === 'dynamic';
|
|
1243
2385
|
},
|
|
2386
|
+
/**
|
|
2387
|
+
* Determines if the children to the container can be clicked/touched
|
|
2388
|
+
* Setting this to false allows PixiJS to bypass a recursive `hitTest` function
|
|
2389
|
+
* @memberof scene.Container#
|
|
2390
|
+
*/
|
|
1244
2391
|
interactiveChildren: true,
|
|
2392
|
+
/**
|
|
2393
|
+
* Interaction shape. Children will be hit first, then this shape will be checked.
|
|
2394
|
+
* Setting this will cause this shape to be checked in hit tests rather than the container's bounds.
|
|
2395
|
+
* @example
|
|
2396
|
+
* import { Rectangle, Sprite } from 'pixi.js';
|
|
2397
|
+
*
|
|
2398
|
+
* const sprite = new Sprite(texture);
|
|
2399
|
+
* sprite.interactive = true;
|
|
2400
|
+
* sprite.hitArea = new Rectangle(0, 0, 100, 100);
|
|
2401
|
+
* @member {IHitArea}
|
|
2402
|
+
* @memberof scene.Container#
|
|
2403
|
+
*/
|
|
1245
2404
|
hitArea: null,
|
|
2405
|
+
/**
|
|
2406
|
+
* Unlike `on` or `addListener` which are methods from EventEmitter, `addEventListener`
|
|
2407
|
+
* seeks to be compatible with the DOM's `addEventListener` with support for options.
|
|
2408
|
+
* @memberof scene.Container
|
|
2409
|
+
* @param type - The type of event to listen to.
|
|
2410
|
+
* @param listener - The listener callback or object.
|
|
2411
|
+
* @param options - Listener options, used for capture phase.
|
|
2412
|
+
* @example
|
|
2413
|
+
* // Tell the user whether they did a single, double, triple, or nth click.
|
|
2414
|
+
* button.addEventListener('click', {
|
|
2415
|
+
* handleEvent(e): {
|
|
2416
|
+
* let prefix;
|
|
2417
|
+
*
|
|
2418
|
+
* switch (e.detail) {
|
|
2419
|
+
* case 1: prefix = 'single'; break;
|
|
2420
|
+
* case 2: prefix = 'double'; break;
|
|
2421
|
+
* case 3: prefix = 'triple'; break;
|
|
2422
|
+
* default: prefix = e.detail + 'th'; break;
|
|
2423
|
+
* }
|
|
2424
|
+
*
|
|
2425
|
+
* console.log('That was a ' + prefix + 'click');
|
|
2426
|
+
* }
|
|
2427
|
+
* });
|
|
2428
|
+
*
|
|
2429
|
+
* // But skip the first click!
|
|
2430
|
+
* button.parent.addEventListener('click', function blockClickOnce(e) {
|
|
2431
|
+
* e.stopImmediatePropagation();
|
|
2432
|
+
* button.parent.removeEventListener('click', blockClickOnce, true);
|
|
2433
|
+
* }, {
|
|
2434
|
+
* capture: true,
|
|
2435
|
+
* });
|
|
2436
|
+
*/
|
|
1246
2437
|
addEventListener(type, listener, options) {
|
|
1247
2438
|
const capture = (typeof options === 'boolean' && options) || (typeof options === 'object' && options.capture);
|
|
1248
2439
|
const signal = typeof options === 'object' ? options.signal : undefined;
|
|
@@ -1263,6 +2454,14 @@ const FederatedContainer = {
|
|
|
1263
2454
|
emitter.on(type, listenerFn, context);
|
|
1264
2455
|
}
|
|
1265
2456
|
},
|
|
2457
|
+
/**
|
|
2458
|
+
* Unlike `off` or `removeListener` which are methods from EventEmitter, `removeEventListener`
|
|
2459
|
+
* seeks to be compatible with the DOM's `removeEventListener` with support for options.
|
|
2460
|
+
* @memberof scene.Container
|
|
2461
|
+
* @param type - The type of event the listener is bound to.
|
|
2462
|
+
* @param listener - The listener callback or object.
|
|
2463
|
+
* @param options - The original listener options. This is required to deregister a capture phase listener.
|
|
2464
|
+
*/
|
|
1266
2465
|
removeEventListener(type, listener, options) {
|
|
1267
2466
|
const capture = (typeof options === 'boolean' && options) || (typeof options === 'object' && options.capture);
|
|
1268
2467
|
const context = typeof listener === 'function' ? undefined : listener;
|
|
@@ -1270,6 +2469,17 @@ const FederatedContainer = {
|
|
|
1270
2469
|
listener = typeof listener === 'function' ? listener : listener.handleEvent;
|
|
1271
2470
|
this.off(type, listener, context);
|
|
1272
2471
|
},
|
|
2472
|
+
/**
|
|
2473
|
+
* Dispatch the event on this {@link Container} using the event's {@link EventBoundary}.
|
|
2474
|
+
*
|
|
2475
|
+
* The target of the event is set to `this` and the `defaultPrevented` flag is cleared before dispatch.
|
|
2476
|
+
* @memberof scene.Container
|
|
2477
|
+
* @param e - The event to dispatch.
|
|
2478
|
+
* @returns Whether the {@link FederatedEvent.preventDefault preventDefault}() method was not invoked.
|
|
2479
|
+
* @example
|
|
2480
|
+
* // Reuse a click event!
|
|
2481
|
+
* button.dispatchEvent(clickEvent);
|
|
2482
|
+
*/
|
|
1273
2483
|
dispatchEvent(e) {
|
|
1274
2484
|
if (!(e instanceof FederatedEvent)) {
|
|
1275
2485
|
throw new Error('Container cannot propagate events outside of the Federated Events API');
|
|
@@ -1282,6 +2492,101 @@ const FederatedContainer = {
|
|
|
1282
2492
|
},
|
|
1283
2493
|
};
|
|
1284
2494
|
|
|
2495
|
+
/* eslint-disable max-len */
|
|
2496
|
+
/**
|
|
2497
|
+
* PixiJS is primarily a rendering system, but it also includes support for interactivity.
|
|
2498
|
+
* Adding support for mouse and touch events to your project is simple and consistent.
|
|
2499
|
+
*
|
|
2500
|
+
* The new event-based system that replaced InteractionManager from v6 has expanded the definition of what a
|
|
2501
|
+
* Container means to be interactive. With this we have introduced `eventMode` which allows you to control
|
|
2502
|
+
* how an object responds to interaction events.
|
|
2503
|
+
* This is similar to the `interactive` property in v6 but with more options.
|
|
2504
|
+
*
|
|
2505
|
+
* <details id="enabling-interaction">
|
|
2506
|
+
* <summary>Enabling Interaction</summary>
|
|
2507
|
+
*
|
|
2508
|
+
* Any Container-derived object (Sprite, Container, etc.) can become interactive simply by setting its `eventMode` property to any of
|
|
2509
|
+
* the {@link events.EventMode} values. Doing so will cause the object to emit interaction events that can be responded to in order to drive your project's behavior.
|
|
2510
|
+
*
|
|
2511
|
+
* Check out the [interaction example code](/examples/events/click).
|
|
2512
|
+
*
|
|
2513
|
+
* Container-derived objects are based on {@link https://www.npmjs.com/package/eventemitter3|EventEmitter3}
|
|
2514
|
+
* so you can use `on()`, `once()`, `off()` to listen to events.
|
|
2515
|
+
*
|
|
2516
|
+
* For example to respond to clicks and taps, bind to an object ike so:
|
|
2517
|
+
*
|
|
2518
|
+
* ```javascript
|
|
2519
|
+
* let sprite = Sprite.from('/some/texture.png');
|
|
2520
|
+
*
|
|
2521
|
+
* sprite.eventMode = 'static'; // similar to `sprite.interactive = true` in v6
|
|
2522
|
+
* sprite.on('pointerdown', (event) => { alert('clicked!'); });
|
|
2523
|
+
* ```
|
|
2524
|
+
*
|
|
2525
|
+
* Check out the **EventTypes** section below for the full list of interaction events supported.
|
|
2526
|
+
* </details>
|
|
2527
|
+
*
|
|
2528
|
+
* <details id="event-modes">
|
|
2529
|
+
* <summary>Event Modes</summary>
|
|
2530
|
+
*
|
|
2531
|
+
* The new event-based system that replaced InteractionManager from v6 has expanded the definition of what a Container
|
|
2532
|
+
* means to be interactive. With this we have introduced `eventMode` which allows you to control how an object responds
|
|
2533
|
+
* to interaction events. This is similar to the `interactive` property in v6 but with more options.
|
|
2534
|
+
*
|
|
2535
|
+
* | event mode | Description |
|
|
2536
|
+
* |---|---|
|
|
2537
|
+
* | `none` | Ignores all interaction events, similar to CSS's `pointer-events: none`, good optimization for non-interactive children |
|
|
2538
|
+
* | `passive` | Does not emit events and ignores hit testing on itself but does allow for events and hit testing only its interactive children. If you want to be compatible with v6, set this as your default `eventMode` (see options in Renderer, Application, etc) |
|
|
2539
|
+
* | `auto` | Does not emit events and but is hit tested if parent is interactive. Same as `interactive = false` in v7 |
|
|
2540
|
+
* | `static` | Emit events and is hit tested. Same as `interaction = true` in v7, useful for objects like buttons that do not move. |
|
|
2541
|
+
* | `dynamic` | Emits events and is hit tested but will also receive mock interaction events fired from a ticker to allow for interaction when the mouse isn't moving. This is useful for elements that independently moving or animating. |
|
|
2542
|
+
* </details>
|
|
2543
|
+
*
|
|
2544
|
+
* <details id="event-types">
|
|
2545
|
+
* <summary>Event Types</summary>
|
|
2546
|
+
*
|
|
2547
|
+
* Pixi supports the following event types for interactive objects:
|
|
2548
|
+
*
|
|
2549
|
+
* | Event Type | Fired When |
|
|
2550
|
+
* |---|---|
|
|
2551
|
+
* | `pointercancel` | Pointer device button is released outside the display object
|
|
2552
|
+
* that initially registered a pointerdown. |
|
|
2553
|
+
* | `pointerdown` | Pointer device button is pressed on the display object. |
|
|
2554
|
+
* | `pointerenter` | Pointer device enters the display object. |
|
|
2555
|
+
* | `pointerleave` | Pointer device leaves the display object. |
|
|
2556
|
+
* | `pointermove` | Pointer device is moved while over the display object. |
|
|
2557
|
+
* | `globalpointermove` | Pointer device is moved, regardless of hit-testing the current object. |
|
|
2558
|
+
* | `pointerout` | Pointer device is moved off the display object. |
|
|
2559
|
+
* | `pointerover` | Pointer device is moved onto the display object. |
|
|
2560
|
+
* | `pointertap` | Pointer device is tapped twice on the display object. |
|
|
2561
|
+
* | `pointerup` | Pointer device button is released over the display object. |
|
|
2562
|
+
* | `pointerupoutside` | Pointer device button is released outside the display object
|
|
2563
|
+
* that initially registered a pointerdown. |
|
|
2564
|
+
* | `mousedown ` | Mouse button is pressed on the display object. |
|
|
2565
|
+
* | `mouseenter` | Mouse cursor enters the display object. |
|
|
2566
|
+
* | `mouseleave` | Mouse cursor leaves the display object. |
|
|
2567
|
+
* | `mousemove ` | Mouse cursor is moved while over the display object. |
|
|
2568
|
+
* | `globalmousemove` | Mouse is moved, regardless of hit-testing the current object. |
|
|
2569
|
+
* | `mouseout ` | Mouse cursor is moved off the display object. |
|
|
2570
|
+
* | `mouseover ` | Mouse cursor is moved onto the display object. |
|
|
2571
|
+
* | `mouseup ` | Mouse button is released over the display object. |
|
|
2572
|
+
* | `mouseupoutside ` | Mouse button is released outside the display object that initially registered a mousedown. |
|
|
2573
|
+
* | `click ` | Mouse button is clicked (pressed and released) over the display object. |
|
|
2574
|
+
* | `touchcancel ` | Touch point is removed outside of the display object that initially registered a touchstart. |
|
|
2575
|
+
* | `touchend ` | Touch point is removed from the display object. |
|
|
2576
|
+
* | `touchendoutside ` | Touch point is removed outside of the display object that initially registered a touchstart. |
|
|
2577
|
+
* | `touchmove ` | Touch point is moved along the display object. |
|
|
2578
|
+
* | `globaltouchmove` | Touch point is moved, regardless of hit-testing the current object. |
|
|
2579
|
+
* | `touchstart ` | Touch point is placed on the display object. |
|
|
2580
|
+
* | `tap ` | Touch point is tapped twice on the display object. |
|
|
2581
|
+
* | `wheel ` | Mouse wheel is spun over the display object. |
|
|
2582
|
+
* | `rightclick ` | Right mouse button is clicked (pressed and released) over the display object. |
|
|
2583
|
+
* | `rightdown ` | Right mouse button is pressed on the display object. |
|
|
2584
|
+
* | `rightup ` | Right mouse button is released over the display object. |
|
|
2585
|
+
* | `rightupoutside ` | Right mouse button is released outside the display object that initially registered a rightdown. |
|
|
2586
|
+
* </details>
|
|
2587
|
+
* @namespace events
|
|
2588
|
+
*/
|
|
2589
|
+
/* eslint-enable max-len */
|
|
1285
2590
|
const init = () => {
|
|
1286
2591
|
extensions.add(EventSystem);
|
|
1287
2592
|
extensions.mixin(Container, FederatedContainer);
|