@react-three/fiber 8.11.8 → 8.11.10
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/CHANGELOG.md +12 -0
- package/dist/declarations/src/core/events.d.ts +71 -71
- package/dist/declarations/src/core/hooks.d.ts +29 -29
- package/dist/declarations/src/core/index.d.ts +57 -57
- package/dist/declarations/src/core/loop.d.ts +14 -14
- package/dist/declarations/src/core/renderer.d.ts +58 -52
- package/dist/declarations/src/core/store.d.ts +95 -95
- package/dist/declarations/src/core/utils.d.ts +89 -89
- package/dist/declarations/src/index.d.ts +12 -12
- package/dist/declarations/src/native/Canvas.d.ts +8 -8
- package/dist/declarations/src/native/events.d.ts +4 -4
- package/dist/declarations/src/native/polyfills.d.ts +1 -1
- package/dist/declarations/src/native.d.ts +12 -12
- package/dist/declarations/src/three-types.d.ts +335 -335
- package/dist/declarations/src/web/Canvas.d.ts +11 -11
- package/dist/declarations/src/web/events.d.ts +4 -4
- package/dist/{index-648b7c70.esm.js → index-9cc932ed.esm.js} +704 -684
- package/dist/{index-ae10f2b1.cjs.prod.js → index-c5f9584c.cjs.prod.js} +704 -684
- package/dist/{index-baacab10.cjs.dev.js → index-cf981775.cjs.dev.js} +704 -684
- package/dist/react-three-fiber.cjs.dev.js +1 -1
- package/dist/react-three-fiber.cjs.prod.js +1 -1
- package/dist/react-three-fiber.esm.js +2 -2
- package/native/dist/react-three-fiber-native.cjs.dev.js +1 -1
- package/native/dist/react-three-fiber-native.cjs.prod.js +1 -1
- package/native/dist/react-three-fiber-native.esm.js +2 -2
- package/package.json +1 -1
|
@@ -37,15 +37,353 @@ var threeTypes = /*#__PURE__*/Object.freeze({
|
|
|
37
37
|
__proto__: null
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
const catalogue = {};
|
|
41
|
+
const extend = objects => void Object.assign(catalogue, objects);
|
|
42
|
+
function createRenderer(_roots, _getEventPriority) {
|
|
43
|
+
function createInstance(type, {
|
|
44
|
+
args = [],
|
|
45
|
+
attach,
|
|
46
|
+
...props
|
|
47
|
+
}, root) {
|
|
48
|
+
let name = `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
49
|
+
let instance;
|
|
50
|
+
if (type === 'primitive') {
|
|
51
|
+
if (props.object === undefined) throw new Error("R3F: Primitives without 'object' are invalid!");
|
|
52
|
+
const object = props.object;
|
|
53
|
+
instance = prepare(object, {
|
|
54
|
+
type,
|
|
55
|
+
root,
|
|
56
|
+
attach,
|
|
57
|
+
primitive: true
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
const target = catalogue[name];
|
|
61
|
+
if (!target) {
|
|
62
|
+
throw new Error(`R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Throw if an object or literal was passed for args
|
|
66
|
+
if (!Array.isArray(args)) throw new Error('R3F: The args prop must be an array!');
|
|
67
|
+
|
|
68
|
+
// Instanciate new object, link it to the root
|
|
69
|
+
// Append memoized props with args so it's not forgotten
|
|
70
|
+
instance = prepare(new target(...args), {
|
|
71
|
+
type,
|
|
72
|
+
root,
|
|
73
|
+
attach,
|
|
74
|
+
// Save args in case we need to reconstruct later for HMR
|
|
75
|
+
memoizedProps: {
|
|
76
|
+
args
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Auto-attach geometries and materials
|
|
82
|
+
if (instance.__r3f.attach === undefined) {
|
|
83
|
+
if (instance instanceof THREE__namespace.BufferGeometry) instance.__r3f.attach = 'geometry';else if (instance instanceof THREE__namespace.Material) instance.__r3f.attach = 'material';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// It should NOT call onUpdate on object instanciation, because it hasn't been added to the
|
|
87
|
+
// view yet. If the callback relies on references for instance, they won't be ready yet, this is
|
|
88
|
+
// why it passes "true" here
|
|
89
|
+
// There is no reason to apply props to injects
|
|
90
|
+
if (name !== 'inject') applyProps$1(instance, props);
|
|
91
|
+
return instance;
|
|
92
|
+
}
|
|
93
|
+
function appendChild(parentInstance, child) {
|
|
94
|
+
let added = false;
|
|
95
|
+
if (child) {
|
|
96
|
+
var _child$__r3f, _parentInstance$__r3f;
|
|
97
|
+
// The attach attribute implies that the object attaches itself on the parent
|
|
98
|
+
if ((_child$__r3f = child.__r3f) != null && _child$__r3f.attach) {
|
|
99
|
+
attach(parentInstance, child, child.__r3f.attach);
|
|
100
|
+
} else if (child.isObject3D && parentInstance.isObject3D) {
|
|
101
|
+
// add in the usual parent-child way
|
|
102
|
+
parentInstance.add(child);
|
|
103
|
+
added = true;
|
|
104
|
+
}
|
|
105
|
+
// This is for anything that used attach, and for non-Object3Ds that don't get attached to props;
|
|
106
|
+
// that is, anything that's a child in React but not a child in the scenegraph.
|
|
107
|
+
if (!added) (_parentInstance$__r3f = parentInstance.__r3f) == null ? void 0 : _parentInstance$__r3f.objects.push(child);
|
|
108
|
+
if (!child.__r3f) prepare(child, {});
|
|
109
|
+
child.__r3f.parent = parentInstance;
|
|
110
|
+
updateInstance(child);
|
|
111
|
+
invalidateInstance(child);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function insertBefore(parentInstance, child, beforeChild) {
|
|
115
|
+
let added = false;
|
|
116
|
+
if (child) {
|
|
117
|
+
var _child$__r3f2, _parentInstance$__r3f2;
|
|
118
|
+
if ((_child$__r3f2 = child.__r3f) != null && _child$__r3f2.attach) {
|
|
119
|
+
attach(parentInstance, child, child.__r3f.attach);
|
|
120
|
+
} else if (child.isObject3D && parentInstance.isObject3D) {
|
|
121
|
+
child.parent = parentInstance;
|
|
122
|
+
child.dispatchEvent({
|
|
123
|
+
type: 'added'
|
|
124
|
+
});
|
|
125
|
+
const restSiblings = parentInstance.children.filter(sibling => sibling !== child);
|
|
126
|
+
const index = restSiblings.indexOf(beforeChild);
|
|
127
|
+
parentInstance.children = [...restSiblings.slice(0, index), child, ...restSiblings.slice(index)];
|
|
128
|
+
added = true;
|
|
129
|
+
}
|
|
130
|
+
if (!added) (_parentInstance$__r3f2 = parentInstance.__r3f) == null ? void 0 : _parentInstance$__r3f2.objects.push(child);
|
|
131
|
+
if (!child.__r3f) prepare(child, {});
|
|
132
|
+
child.__r3f.parent = parentInstance;
|
|
133
|
+
updateInstance(child);
|
|
134
|
+
invalidateInstance(child);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function removeRecursive(array, parent, dispose = false) {
|
|
138
|
+
if (array) [...array].forEach(child => removeChild(parent, child, dispose));
|
|
139
|
+
}
|
|
140
|
+
function removeChild(parentInstance, child, dispose) {
|
|
141
|
+
if (child) {
|
|
142
|
+
var _parentInstance$__r3f3, _child$__r3f3, _child$__r3f5;
|
|
143
|
+
// Clear the parent reference
|
|
144
|
+
if (child.__r3f) child.__r3f.parent = null;
|
|
145
|
+
// Remove child from the parents objects
|
|
146
|
+
if ((_parentInstance$__r3f3 = parentInstance.__r3f) != null && _parentInstance$__r3f3.objects) parentInstance.__r3f.objects = parentInstance.__r3f.objects.filter(x => x !== child);
|
|
147
|
+
// Remove attachment
|
|
148
|
+
if ((_child$__r3f3 = child.__r3f) != null && _child$__r3f3.attach) {
|
|
149
|
+
detach(parentInstance, child, child.__r3f.attach);
|
|
150
|
+
} else if (child.isObject3D && parentInstance.isObject3D) {
|
|
151
|
+
var _child$__r3f4;
|
|
152
|
+
parentInstance.remove(child);
|
|
153
|
+
// Remove interactivity
|
|
154
|
+
if ((_child$__r3f4 = child.__r3f) != null && _child$__r3f4.root) {
|
|
155
|
+
removeInteractivity(child.__r3f.root, child);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Allow objects to bail out of recursive dispose altogether by passing dispose={null}
|
|
160
|
+
// Never dispose of primitives because their state may be kept outside of React!
|
|
161
|
+
// In order for an object to be able to dispose it has to have
|
|
162
|
+
// - a dispose method,
|
|
163
|
+
// - it cannot be a <primitive object={...} />
|
|
164
|
+
// - it cannot be a THREE.Scene, because three has broken it's own api
|
|
165
|
+
//
|
|
166
|
+
// Since disposal is recursive, we can check the optional dispose arg, which will be undefined
|
|
167
|
+
// when the reconciler calls it, but then carry our own check recursively
|
|
168
|
+
const isPrimitive = (_child$__r3f5 = child.__r3f) == null ? void 0 : _child$__r3f5.primitive;
|
|
169
|
+
const shouldDispose = dispose === undefined ? child.dispose !== null && !isPrimitive : dispose;
|
|
170
|
+
|
|
171
|
+
// Remove nested child objects. Primitives should not have objects and children that are
|
|
172
|
+
// attached to them declaratively ...
|
|
173
|
+
if (!isPrimitive) {
|
|
174
|
+
var _child$__r3f6;
|
|
175
|
+
removeRecursive((_child$__r3f6 = child.__r3f) == null ? void 0 : _child$__r3f6.objects, child, shouldDispose);
|
|
176
|
+
removeRecursive(child.children, child, shouldDispose);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Remove references
|
|
180
|
+
if (child.__r3f) {
|
|
181
|
+
delete child.__r3f.root;
|
|
182
|
+
delete child.__r3f.objects;
|
|
183
|
+
delete child.__r3f.handlers;
|
|
184
|
+
delete child.__r3f.memoizedProps;
|
|
185
|
+
if (!isPrimitive) delete child.__r3f;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Dispose item whenever the reconciler feels like it
|
|
189
|
+
if (shouldDispose && child.dispose && child.type !== 'Scene') {
|
|
190
|
+
scheduler.unstable_scheduleCallback(scheduler.unstable_IdlePriority, () => {
|
|
191
|
+
try {
|
|
192
|
+
child.dispose();
|
|
193
|
+
} catch (e) {
|
|
194
|
+
/* ... */
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
invalidateInstance(parentInstance);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function switchInstance(instance, type, newProps, fiber) {
|
|
202
|
+
var _instance$__r3f;
|
|
203
|
+
const parent = (_instance$__r3f = instance.__r3f) == null ? void 0 : _instance$__r3f.parent;
|
|
204
|
+
if (!parent) return;
|
|
205
|
+
const newInstance = createInstance(type, newProps, instance.__r3f.root);
|
|
206
|
+
|
|
207
|
+
// https://github.com/pmndrs/react-three-fiber/issues/1348
|
|
208
|
+
// When args change the instance has to be re-constructed, which then
|
|
209
|
+
// forces r3f to re-parent the children and non-scene objects
|
|
210
|
+
if (instance.children) {
|
|
211
|
+
for (const child of instance.children) {
|
|
212
|
+
if (child.__r3f) appendChild(newInstance, child);
|
|
213
|
+
}
|
|
214
|
+
instance.children = instance.children.filter(child => !child.__r3f);
|
|
215
|
+
}
|
|
216
|
+
instance.__r3f.objects.forEach(child => appendChild(newInstance, child));
|
|
217
|
+
instance.__r3f.objects = [];
|
|
218
|
+
if (!instance.__r3f.autoRemovedBeforeAppend) {
|
|
219
|
+
removeChild(parent, instance);
|
|
220
|
+
}
|
|
221
|
+
if (newInstance.parent) {
|
|
222
|
+
newInstance.__r3f.autoRemovedBeforeAppend = true;
|
|
223
|
+
}
|
|
224
|
+
appendChild(parent, newInstance);
|
|
225
|
+
|
|
226
|
+
// Re-bind event handlers
|
|
227
|
+
if (newInstance.raycast && newInstance.__r3f.eventCount) {
|
|
228
|
+
const rootState = newInstance.__r3f.root.getState();
|
|
229
|
+
rootState.internal.interaction.push(newInstance);
|
|
230
|
+
}
|
|
231
|
+
[fiber, fiber.alternate].forEach(fiber => {
|
|
232
|
+
if (fiber !== null) {
|
|
233
|
+
fiber.stateNode = newInstance;
|
|
234
|
+
if (fiber.ref) {
|
|
235
|
+
if (typeof fiber.ref === 'function') fiber.ref(newInstance);else fiber.ref.current = newInstance;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Don't handle text instances, warn on undefined behavior
|
|
242
|
+
const handleTextInstance = () => console.warn('Text is not allowed in the R3F tree! This could be stray whitespace or characters.');
|
|
243
|
+
const reconciler = Reconciler__default["default"]({
|
|
244
|
+
createInstance,
|
|
245
|
+
removeChild,
|
|
246
|
+
appendChild,
|
|
247
|
+
appendInitialChild: appendChild,
|
|
248
|
+
insertBefore,
|
|
249
|
+
supportsMutation: true,
|
|
250
|
+
isPrimaryRenderer: false,
|
|
251
|
+
supportsPersistence: false,
|
|
252
|
+
supportsHydration: false,
|
|
253
|
+
noTimeout: -1,
|
|
254
|
+
appendChildToContainer: (container, child) => {
|
|
255
|
+
if (!child) return;
|
|
256
|
+
|
|
257
|
+
// Don't append to unmounted container
|
|
258
|
+
const scene = container.getState().scene;
|
|
259
|
+
if (!scene.__r3f) return;
|
|
260
|
+
|
|
261
|
+
// Link current root to the default scene
|
|
262
|
+
scene.__r3f.root = container;
|
|
263
|
+
appendChild(scene, child);
|
|
264
|
+
},
|
|
265
|
+
removeChildFromContainer: (container, child) => {
|
|
266
|
+
if (!child) return;
|
|
267
|
+
removeChild(container.getState().scene, child);
|
|
268
|
+
},
|
|
269
|
+
insertInContainerBefore: (container, child, beforeChild) => {
|
|
270
|
+
if (!child || !beforeChild) return;
|
|
271
|
+
|
|
272
|
+
// Don't append to unmounted container
|
|
273
|
+
const scene = container.getState().scene;
|
|
274
|
+
if (!scene.__r3f) return;
|
|
275
|
+
insertBefore(scene, child, beforeChild);
|
|
276
|
+
},
|
|
277
|
+
getRootHostContext: () => null,
|
|
278
|
+
getChildHostContext: parentHostContext => parentHostContext,
|
|
279
|
+
finalizeInitialChildren(instance) {
|
|
280
|
+
var _instance$__r3f2;
|
|
281
|
+
const localState = (_instance$__r3f2 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f2 : {};
|
|
282
|
+
// https://github.com/facebook/react/issues/20271
|
|
283
|
+
// Returning true will trigger commitMount
|
|
284
|
+
return Boolean(localState.handlers);
|
|
285
|
+
},
|
|
286
|
+
prepareUpdate(instance, _type, oldProps, newProps) {
|
|
287
|
+
// Create diff-sets
|
|
288
|
+
if (instance.__r3f.primitive && newProps.object && newProps.object !== instance) {
|
|
289
|
+
return [true];
|
|
290
|
+
} else {
|
|
291
|
+
// This is a data object, let's extract critical information about it
|
|
292
|
+
const {
|
|
293
|
+
args: argsNew = [],
|
|
294
|
+
children: cN,
|
|
295
|
+
...restNew
|
|
296
|
+
} = newProps;
|
|
297
|
+
const {
|
|
298
|
+
args: argsOld = [],
|
|
299
|
+
children: cO,
|
|
300
|
+
...restOld
|
|
301
|
+
} = oldProps;
|
|
302
|
+
|
|
303
|
+
// Throw if an object or literal was passed for args
|
|
304
|
+
if (!Array.isArray(argsNew)) throw new Error('R3F: the args prop must be an array!');
|
|
305
|
+
|
|
306
|
+
// If it has new props or arguments, then it needs to be re-instantiated
|
|
307
|
+
if (argsNew.some((value, index) => value !== argsOld[index])) return [true];
|
|
308
|
+
// Create a diff-set, flag if there are any changes
|
|
309
|
+
const diff = diffProps(instance, restNew, restOld, true);
|
|
310
|
+
if (diff.changes.length) return [false, diff];
|
|
311
|
+
|
|
312
|
+
// Otherwise do not touch the instance
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
},
|
|
316
|
+
commitUpdate(instance, [reconstruct, diff], type, _oldProps, newProps, fiber) {
|
|
317
|
+
// Reconstruct when args or <primitive object={...} have changes
|
|
318
|
+
if (reconstruct) switchInstance(instance, type, newProps, fiber);
|
|
319
|
+
// Otherwise just overwrite props
|
|
320
|
+
else applyProps$1(instance, diff);
|
|
321
|
+
},
|
|
322
|
+
commitMount(instance, _type, _props, _int) {
|
|
323
|
+
var _instance$__r3f3;
|
|
324
|
+
// https://github.com/facebook/react/issues/20271
|
|
325
|
+
// This will make sure events are only added once to the central container
|
|
326
|
+
const localState = (_instance$__r3f3 = instance.__r3f) != null ? _instance$__r3f3 : {};
|
|
327
|
+
if (instance.raycast && localState.handlers && localState.eventCount) {
|
|
328
|
+
instance.__r3f.root.getState().internal.interaction.push(instance);
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
getPublicInstance: instance => instance,
|
|
332
|
+
prepareForCommit: () => null,
|
|
333
|
+
preparePortalMount: container => prepare(container.getState().scene),
|
|
334
|
+
resetAfterCommit: () => {},
|
|
335
|
+
shouldSetTextContent: () => false,
|
|
336
|
+
clearContainer: () => false,
|
|
337
|
+
hideInstance(instance) {
|
|
338
|
+
var _instance$__r3f4;
|
|
339
|
+
// Detach while the instance is hidden
|
|
340
|
+
const {
|
|
341
|
+
attach: type,
|
|
342
|
+
parent
|
|
343
|
+
} = (_instance$__r3f4 = instance.__r3f) != null ? _instance$__r3f4 : {};
|
|
344
|
+
if (type && parent) detach(parent, instance, type);
|
|
345
|
+
if (instance.isObject3D) instance.visible = false;
|
|
346
|
+
invalidateInstance(instance);
|
|
347
|
+
},
|
|
348
|
+
unhideInstance(instance, props) {
|
|
349
|
+
var _instance$__r3f5;
|
|
350
|
+
// Re-attach when the instance is unhidden
|
|
351
|
+
const {
|
|
352
|
+
attach: type,
|
|
353
|
+
parent
|
|
354
|
+
} = (_instance$__r3f5 = instance.__r3f) != null ? _instance$__r3f5 : {};
|
|
355
|
+
if (type && parent) attach(parent, instance, type);
|
|
356
|
+
if (instance.isObject3D && props.visible == null || props.visible) instance.visible = true;
|
|
357
|
+
invalidateInstance(instance);
|
|
358
|
+
},
|
|
359
|
+
createTextInstance: handleTextInstance,
|
|
360
|
+
hideTextInstance: handleTextInstance,
|
|
361
|
+
unhideTextInstance: handleTextInstance,
|
|
362
|
+
// https://github.com/pmndrs/react-three-fiber/pull/2360#discussion_r916356874
|
|
363
|
+
// @ts-ignore
|
|
364
|
+
getCurrentEventPriority: () => _getEventPriority ? _getEventPriority() : constants.DefaultEventPriority,
|
|
365
|
+
beforeActiveInstanceBlur: () => {},
|
|
366
|
+
afterActiveInstanceBlur: () => {},
|
|
367
|
+
detachDeletedInstance: () => {},
|
|
368
|
+
now: typeof performance !== 'undefined' && is.fun(performance.now) ? performance.now : is.fun(Date.now) ? Date.now : () => 0,
|
|
369
|
+
// https://github.com/pmndrs/react-three-fiber/pull/2360#discussion_r920883503
|
|
370
|
+
scheduleTimeout: is.fun(setTimeout) ? setTimeout : undefined,
|
|
371
|
+
cancelTimeout: is.fun(clearTimeout) ? clearTimeout : undefined
|
|
372
|
+
});
|
|
373
|
+
return {
|
|
374
|
+
reconciler,
|
|
375
|
+
applyProps: applyProps$1
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
40
379
|
var _window$document, _window$navigator;
|
|
41
|
-
/**
|
|
42
|
-
* Safely accesses a deeply-nested value on an object to get around static bundler analysis.
|
|
43
|
-
*/
|
|
44
|
-
const getDeep = (obj, ...keys) => keys.reduce((acc, key) => acc == null ? void 0 : acc[key], obj);
|
|
45
380
|
/**
|
|
46
381
|
* The current THREE.ColorManagement instance, if present.
|
|
47
382
|
*/
|
|
48
|
-
const
|
|
383
|
+
const getColorManagement = () => {
|
|
384
|
+
var _ColorManagement;
|
|
385
|
+
return (_ColorManagement = catalogue.ColorManagement) != null ? _ColorManagement : null;
|
|
386
|
+
};
|
|
49
387
|
const isOrthographicCamera = def => def && def.isOrthographicCamera;
|
|
50
388
|
const isRef = obj => obj && obj.hasOwnProperty('current');
|
|
51
389
|
|
|
@@ -384,7 +722,7 @@ function applyProps$1(instance, data) {
|
|
|
384
722
|
// For versions of three which don't support THREE.ColorManagement,
|
|
385
723
|
// Auto-convert sRGB colors
|
|
386
724
|
// https://github.com/pmndrs/react-three-fiber/issues/344
|
|
387
|
-
if (!
|
|
725
|
+
if (!getColorManagement() && !rootState.linear && isColor) targetProp.convertSRGBToLinear();
|
|
388
726
|
}
|
|
389
727
|
// Else, just overwrite the value
|
|
390
728
|
} else {
|
|
@@ -469,724 +807,405 @@ function getEventPriority() {
|
|
|
469
807
|
case 'wheel':
|
|
470
808
|
return constants.ContinuousEventPriority;
|
|
471
809
|
default:
|
|
472
|
-
return constants.DefaultEventPriority;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Release pointer captures.
|
|
478
|
-
* This is called by releasePointerCapture in the API, and when an object is removed.
|
|
479
|
-
*/
|
|
480
|
-
function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
|
|
481
|
-
const captureData = captures.get(obj);
|
|
482
|
-
if (captureData) {
|
|
483
|
-
captures.delete(obj);
|
|
484
|
-
// If this was the last capturing object for this pointer
|
|
485
|
-
if (captures.size === 0) {
|
|
486
|
-
capturedMap.delete(pointerId);
|
|
487
|
-
captureData.target.releasePointerCapture(pointerId);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
function removeInteractivity(store, object) {
|
|
492
|
-
const {
|
|
493
|
-
internal
|
|
494
|
-
} = store.getState();
|
|
495
|
-
// Removes every trace of an object from the data store
|
|
496
|
-
internal.interaction = internal.interaction.filter(o => o !== object);
|
|
497
|
-
internal.initialHits = internal.initialHits.filter(o => o !== object);
|
|
498
|
-
internal.hovered.forEach((value, key) => {
|
|
499
|
-
if (value.eventObject === object || value.object === object) {
|
|
500
|
-
// Clear out intersects, they are outdated by now
|
|
501
|
-
internal.hovered.delete(key);
|
|
502
|
-
}
|
|
503
|
-
});
|
|
504
|
-
internal.capturedMap.forEach((captures, pointerId) => {
|
|
505
|
-
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
function createEvents(store) {
|
|
509
|
-
/** Calculates delta */
|
|
510
|
-
function calculateDistance(event) {
|
|
511
|
-
const {
|
|
512
|
-
internal
|
|
513
|
-
} = store.getState();
|
|
514
|
-
const dx = event.offsetX - internal.initialClick[0];
|
|
515
|
-
const dy = event.offsetY - internal.initialClick[1];
|
|
516
|
-
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
|
|
520
|
-
function filterPointerEvents(objects) {
|
|
521
|
-
return objects.filter(obj => ['Move', 'Over', 'Enter', 'Out', 'Leave'].some(name => {
|
|
522
|
-
var _r3f;
|
|
523
|
-
return (_r3f = obj.__r3f) == null ? void 0 : _r3f.handlers['onPointer' + name];
|
|
524
|
-
}));
|
|
525
|
-
}
|
|
526
|
-
function intersect(event, filter) {
|
|
527
|
-
const state = store.getState();
|
|
528
|
-
const duplicates = new Set();
|
|
529
|
-
const intersections = [];
|
|
530
|
-
// Allow callers to eliminate event objects
|
|
531
|
-
const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
|
|
532
|
-
// Reset all raycaster cameras to undefined
|
|
533
|
-
for (let i = 0; i < eventsObjects.length; i++) {
|
|
534
|
-
const state = getRootState(eventsObjects[i]);
|
|
535
|
-
if (state) {
|
|
536
|
-
state.raycaster.camera = undefined;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
if (!state.previousRoot) {
|
|
540
|
-
// Make sure root-level pointer and ray are set up
|
|
541
|
-
state.events.compute == null ? void 0 : state.events.compute(event, state);
|
|
542
|
-
}
|
|
543
|
-
function handleRaycast(obj) {
|
|
544
|
-
const state = getRootState(obj);
|
|
545
|
-
// Skip event handling when noEvents is set, or when the raycasters camera is null
|
|
546
|
-
if (!state || !state.events.enabled || state.raycaster.camera === null) return [];
|
|
547
|
-
|
|
548
|
-
// When the camera is undefined we have to call the event layers update function
|
|
549
|
-
if (state.raycaster.camera === undefined) {
|
|
550
|
-
var _state$previousRoot;
|
|
551
|
-
state.events.compute == null ? void 0 : state.events.compute(event, state, (_state$previousRoot = state.previousRoot) == null ? void 0 : _state$previousRoot.getState());
|
|
552
|
-
// If the camera is still undefined we have to skip this layer entirely
|
|
553
|
-
if (state.raycaster.camera === undefined) state.raycaster.camera = null;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// Intersect object by object
|
|
557
|
-
return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : [];
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// Collect events
|
|
561
|
-
let hits = eventsObjects
|
|
562
|
-
// Intersect objects
|
|
563
|
-
.flatMap(handleRaycast)
|
|
564
|
-
// Sort by event priority and distance
|
|
565
|
-
.sort((a, b) => {
|
|
566
|
-
const aState = getRootState(a.object);
|
|
567
|
-
const bState = getRootState(b.object);
|
|
568
|
-
if (!aState || !bState) return a.distance - b.distance;
|
|
569
|
-
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
570
|
-
})
|
|
571
|
-
// Filter out duplicates
|
|
572
|
-
.filter(item => {
|
|
573
|
-
const id = makeId(item);
|
|
574
|
-
if (duplicates.has(id)) return false;
|
|
575
|
-
duplicates.add(id);
|
|
576
|
-
return true;
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
// https://github.com/mrdoob/three.js/issues/16031
|
|
580
|
-
// Allow custom userland intersect sort order, this likely only makes sense on the root filter
|
|
581
|
-
if (state.events.filter) hits = state.events.filter(hits, state);
|
|
582
|
-
|
|
583
|
-
// Bubble up the events, find the event source (eventObject)
|
|
584
|
-
for (const hit of hits) {
|
|
585
|
-
let eventObject = hit.object;
|
|
586
|
-
// Bubble event up
|
|
587
|
-
while (eventObject) {
|
|
588
|
-
var _r3f2;
|
|
589
|
-
if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({
|
|
590
|
-
...hit,
|
|
591
|
-
eventObject
|
|
592
|
-
});
|
|
593
|
-
eventObject = eventObject.parent;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
// If the interaction is captured, make all capturing targets part of the intersect.
|
|
598
|
-
if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
|
|
599
|
-
for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
|
|
600
|
-
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
return intersections;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/** Handles intersections by forwarding them to handlers */
|
|
607
|
-
function handleIntersects(intersections, event, delta, callback) {
|
|
608
|
-
const rootState = store.getState();
|
|
609
|
-
|
|
610
|
-
// If anything has been found, forward it to the event listeners
|
|
611
|
-
if (intersections.length) {
|
|
612
|
-
const localState = {
|
|
613
|
-
stopped: false
|
|
614
|
-
};
|
|
615
|
-
for (const hit of intersections) {
|
|
616
|
-
const state = getRootState(hit.object) || rootState;
|
|
617
|
-
const {
|
|
618
|
-
raycaster,
|
|
619
|
-
pointer,
|
|
620
|
-
camera,
|
|
621
|
-
internal
|
|
622
|
-
} = state;
|
|
623
|
-
const unprojectedPoint = new THREE__namespace.Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
624
|
-
const hasPointerCapture = id => {
|
|
625
|
-
var _internal$capturedMap, _internal$capturedMap2;
|
|
626
|
-
return (_internal$capturedMap = (_internal$capturedMap2 = internal.capturedMap.get(id)) == null ? void 0 : _internal$capturedMap2.has(hit.eventObject)) != null ? _internal$capturedMap : false;
|
|
627
|
-
};
|
|
628
|
-
const setPointerCapture = id => {
|
|
629
|
-
const captureData = {
|
|
630
|
-
intersection: hit,
|
|
631
|
-
target: event.target
|
|
632
|
-
};
|
|
633
|
-
if (internal.capturedMap.has(id)) {
|
|
634
|
-
// if the pointerId was previously captured, we add the hit to the
|
|
635
|
-
// event capturedMap.
|
|
636
|
-
internal.capturedMap.get(id).set(hit.eventObject, captureData);
|
|
637
|
-
} else {
|
|
638
|
-
// if the pointerId was not previously captured, we create a map
|
|
639
|
-
// containing the hitObject, and the hit. hitObject is used for
|
|
640
|
-
// faster access.
|
|
641
|
-
internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
|
|
642
|
-
}
|
|
643
|
-
event.target.setPointerCapture(id);
|
|
644
|
-
};
|
|
645
|
-
const releasePointerCapture = id => {
|
|
646
|
-
const captures = internal.capturedMap.get(id);
|
|
647
|
-
if (captures) {
|
|
648
|
-
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
649
|
-
}
|
|
650
|
-
};
|
|
651
|
-
|
|
652
|
-
// Add native event props
|
|
653
|
-
let extractEventProps = {};
|
|
654
|
-
// This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
|
|
655
|
-
for (let prop in event) {
|
|
656
|
-
let property = event[prop];
|
|
657
|
-
// Only copy over atomics, leave functions alone as these should be
|
|
658
|
-
// called as event.nativeEvent.fn()
|
|
659
|
-
if (typeof property !== 'function') extractEventProps[prop] = property;
|
|
660
|
-
}
|
|
661
|
-
let raycastEvent = {
|
|
662
|
-
...hit,
|
|
663
|
-
...extractEventProps,
|
|
664
|
-
pointer,
|
|
665
|
-
intersections,
|
|
666
|
-
stopped: localState.stopped,
|
|
667
|
-
delta,
|
|
668
|
-
unprojectedPoint,
|
|
669
|
-
ray: raycaster.ray,
|
|
670
|
-
camera: camera,
|
|
671
|
-
// Hijack stopPropagation, which just sets a flag
|
|
672
|
-
stopPropagation() {
|
|
673
|
-
// https://github.com/pmndrs/react-three-fiber/issues/596
|
|
674
|
-
// Events are not allowed to stop propagation if the pointer has been captured
|
|
675
|
-
const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId);
|
|
676
|
-
|
|
677
|
-
// We only authorize stopPropagation...
|
|
678
|
-
if (
|
|
679
|
-
// ...if this pointer hasn't been captured
|
|
680
|
-
!capturesForPointer ||
|
|
681
|
-
// ... or if the hit object is capturing the pointer
|
|
682
|
-
capturesForPointer.has(hit.eventObject)) {
|
|
683
|
-
raycastEvent.stopped = localState.stopped = true;
|
|
684
|
-
// Propagation is stopped, remove all other hover records
|
|
685
|
-
// An event handler is only allowed to flush other handlers if it is hovered itself
|
|
686
|
-
if (internal.hovered.size && Array.from(internal.hovered.values()).find(i => i.eventObject === hit.eventObject)) {
|
|
687
|
-
// Objects cannot flush out higher up objects that have already caught the event
|
|
688
|
-
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
689
|
-
cancelPointer([...higher, hit]);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
},
|
|
693
|
-
// there should be a distinction between target and currentTarget
|
|
694
|
-
target: {
|
|
695
|
-
hasPointerCapture,
|
|
696
|
-
setPointerCapture,
|
|
697
|
-
releasePointerCapture
|
|
698
|
-
},
|
|
699
|
-
currentTarget: {
|
|
700
|
-
hasPointerCapture,
|
|
701
|
-
setPointerCapture,
|
|
702
|
-
releasePointerCapture
|
|
703
|
-
},
|
|
704
|
-
nativeEvent: event
|
|
705
|
-
};
|
|
706
|
-
|
|
707
|
-
// Call subscribers
|
|
708
|
-
callback(raycastEvent);
|
|
709
|
-
// Event bubbling may be interrupted by stopPropagation
|
|
710
|
-
if (localState.stopped === true) break;
|
|
711
|
-
}
|
|
810
|
+
return constants.DefaultEventPriority;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Release pointer captures.
|
|
816
|
+
* This is called by releasePointerCapture in the API, and when an object is removed.
|
|
817
|
+
*/
|
|
818
|
+
function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
|
|
819
|
+
const captureData = captures.get(obj);
|
|
820
|
+
if (captureData) {
|
|
821
|
+
captures.delete(obj);
|
|
822
|
+
// If this was the last capturing object for this pointer
|
|
823
|
+
if (captures.size === 0) {
|
|
824
|
+
capturedMap.delete(pointerId);
|
|
825
|
+
captureData.target.releasePointerCapture(pointerId);
|
|
712
826
|
}
|
|
713
|
-
return intersections;
|
|
714
827
|
}
|
|
715
|
-
|
|
828
|
+
}
|
|
829
|
+
function removeInteractivity(store, object) {
|
|
830
|
+
const {
|
|
831
|
+
internal
|
|
832
|
+
} = store.getState();
|
|
833
|
+
// Removes every trace of an object from the data store
|
|
834
|
+
internal.interaction = internal.interaction.filter(o => o !== object);
|
|
835
|
+
internal.initialHits = internal.initialHits.filter(o => o !== object);
|
|
836
|
+
internal.hovered.forEach((value, key) => {
|
|
837
|
+
if (value.eventObject === object || value.object === object) {
|
|
838
|
+
// Clear out intersects, they are outdated by now
|
|
839
|
+
internal.hovered.delete(key);
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
internal.capturedMap.forEach((captures, pointerId) => {
|
|
843
|
+
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
function createEvents(store) {
|
|
847
|
+
/** Calculates delta */
|
|
848
|
+
function calculateDistance(event) {
|
|
716
849
|
const {
|
|
717
850
|
internal
|
|
718
851
|
} = store.getState();
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (!intersections.length || !intersections.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
|
|
723
|
-
const eventObject = hoveredObj.eventObject;
|
|
724
|
-
const instance = eventObject.__r3f;
|
|
725
|
-
const handlers = instance == null ? void 0 : instance.handlers;
|
|
726
|
-
internal.hovered.delete(makeId(hoveredObj));
|
|
727
|
-
if (instance != null && instance.eventCount) {
|
|
728
|
-
// Clear out intersects, they are outdated by now
|
|
729
|
-
const data = {
|
|
730
|
-
...hoveredObj,
|
|
731
|
-
intersections
|
|
732
|
-
};
|
|
733
|
-
handlers.onPointerOut == null ? void 0 : handlers.onPointerOut(data);
|
|
734
|
-
handlers.onPointerLeave == null ? void 0 : handlers.onPointerLeave(data);
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
function pointerMissed(event, objects) {
|
|
740
|
-
for (let i = 0; i < objects.length; i++) {
|
|
741
|
-
const instance = objects[i].__r3f;
|
|
742
|
-
instance == null ? void 0 : instance.handlers.onPointerMissed == null ? void 0 : instance.handlers.onPointerMissed(event);
|
|
743
|
-
}
|
|
852
|
+
const dx = event.offsetX - internal.initialClick[0];
|
|
853
|
+
const dy = event.offsetY - internal.initialClick[1];
|
|
854
|
+
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
744
855
|
}
|
|
745
|
-
function handlePointer(name) {
|
|
746
|
-
// Deal with cancelation
|
|
747
|
-
switch (name) {
|
|
748
|
-
case 'onPointerLeave':
|
|
749
|
-
case 'onPointerCancel':
|
|
750
|
-
return () => cancelPointer([]);
|
|
751
|
-
case 'onLostPointerCapture':
|
|
752
|
-
return event => {
|
|
753
|
-
const {
|
|
754
|
-
internal
|
|
755
|
-
} = store.getState();
|
|
756
|
-
if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
|
|
757
|
-
// If the object event interface had onLostPointerCapture, we'd call it here on every
|
|
758
|
-
// object that's getting removed.
|
|
759
|
-
internal.capturedMap.delete(event.pointerId);
|
|
760
|
-
cancelPointer([]);
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
// Any other pointer goes here ...
|
|
766
|
-
return function handleEvent(event) {
|
|
767
|
-
const {
|
|
768
|
-
onPointerMissed,
|
|
769
|
-
internal
|
|
770
|
-
} = store.getState();
|
|
771
|
-
|
|
772
|
-
// prepareRay(event)
|
|
773
|
-
internal.lastEvent.current = event;
|
|
774
|
-
|
|
775
|
-
// Get fresh intersects
|
|
776
|
-
const isPointerMove = name === 'onPointerMove';
|
|
777
|
-
const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
|
|
778
|
-
const filter = isPointerMove ? filterPointerEvents : undefined;
|
|
779
|
-
const hits = intersect(event, filter);
|
|
780
|
-
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
781
|
-
|
|
782
|
-
// Save initial coordinates on pointer-down
|
|
783
|
-
if (name === 'onPointerDown') {
|
|
784
|
-
internal.initialClick = [event.offsetX, event.offsetY];
|
|
785
|
-
internal.initialHits = hits.map(hit => hit.eventObject);
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// If a click yields no results, pass it back to the user as a miss
|
|
789
|
-
// Missed events have to come first in order to establish user-land side-effect clean up
|
|
790
|
-
if (isClickEvent && !hits.length) {
|
|
791
|
-
if (delta <= 2) {
|
|
792
|
-
pointerMissed(event, internal.interaction);
|
|
793
|
-
if (onPointerMissed) onPointerMissed(event);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
// Take care of unhover
|
|
797
|
-
if (isPointerMove) cancelPointer(hits);
|
|
798
|
-
function onIntersect(data) {
|
|
799
|
-
const eventObject = data.eventObject;
|
|
800
|
-
const instance = eventObject.__r3f;
|
|
801
|
-
const handlers = instance == null ? void 0 : instance.handlers;
|
|
802
856
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
const id = makeId(data);
|
|
810
|
-
const hoveredItem = internal.hovered.get(id);
|
|
811
|
-
if (!hoveredItem) {
|
|
812
|
-
// If the object wasn't previously hovered, book it and call its handler
|
|
813
|
-
internal.hovered.set(id, data);
|
|
814
|
-
handlers.onPointerOver == null ? void 0 : handlers.onPointerOver(data);
|
|
815
|
-
handlers.onPointerEnter == null ? void 0 : handlers.onPointerEnter(data);
|
|
816
|
-
} else if (hoveredItem.stopped) {
|
|
817
|
-
// If the object was previously hovered and stopped, we shouldn't allow other items to proceed
|
|
818
|
-
data.stopPropagation();
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
// Call mouse move
|
|
822
|
-
handlers.onPointerMove == null ? void 0 : handlers.onPointerMove(data);
|
|
823
|
-
} else {
|
|
824
|
-
// All other events ...
|
|
825
|
-
const handler = handlers[name];
|
|
826
|
-
if (handler) {
|
|
827
|
-
// Forward all events back to their respective handlers with the exception of click events,
|
|
828
|
-
// which must use the initial target
|
|
829
|
-
if (!isClickEvent || internal.initialHits.includes(eventObject)) {
|
|
830
|
-
// Missed events have to come first
|
|
831
|
-
pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
|
|
832
|
-
// Now call the handler
|
|
833
|
-
handler(data);
|
|
834
|
-
}
|
|
835
|
-
} else {
|
|
836
|
-
// Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
|
|
837
|
-
if (isClickEvent && internal.initialHits.includes(eventObject)) {
|
|
838
|
-
pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
handleIntersects(hits, event, delta, onIntersect);
|
|
844
|
-
};
|
|
857
|
+
/** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
|
|
858
|
+
function filterPointerEvents(objects) {
|
|
859
|
+
return objects.filter(obj => ['Move', 'Over', 'Enter', 'Out', 'Leave'].some(name => {
|
|
860
|
+
var _r3f;
|
|
861
|
+
return (_r3f = obj.__r3f) == null ? void 0 : _r3f.handlers['onPointer' + name];
|
|
862
|
+
}));
|
|
845
863
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
function createInstance(type, {
|
|
858
|
-
args = [],
|
|
859
|
-
attach,
|
|
860
|
-
...props
|
|
861
|
-
}, root) {
|
|
862
|
-
let name = `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
863
|
-
let instance;
|
|
864
|
-
if (type === 'primitive') {
|
|
865
|
-
if (props.object === undefined) throw new Error("R3F: Primitives without 'object' are invalid!");
|
|
866
|
-
const object = props.object;
|
|
867
|
-
instance = prepare(object, {
|
|
868
|
-
type,
|
|
869
|
-
root,
|
|
870
|
-
attach,
|
|
871
|
-
primitive: true
|
|
872
|
-
});
|
|
873
|
-
} else {
|
|
874
|
-
const target = catalogue[name];
|
|
875
|
-
if (!target) {
|
|
876
|
-
throw new Error(`R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`);
|
|
864
|
+
function intersect(event, filter) {
|
|
865
|
+
const state = store.getState();
|
|
866
|
+
const duplicates = new Set();
|
|
867
|
+
const intersections = [];
|
|
868
|
+
// Allow callers to eliminate event objects
|
|
869
|
+
const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
|
|
870
|
+
// Reset all raycaster cameras to undefined
|
|
871
|
+
for (let i = 0; i < eventsObjects.length; i++) {
|
|
872
|
+
const state = getRootState(eventsObjects[i]);
|
|
873
|
+
if (state) {
|
|
874
|
+
state.raycaster.camera = undefined;
|
|
877
875
|
}
|
|
876
|
+
}
|
|
877
|
+
if (!state.previousRoot) {
|
|
878
|
+
// Make sure root-level pointer and ray are set up
|
|
879
|
+
state.events.compute == null ? void 0 : state.events.compute(event, state);
|
|
880
|
+
}
|
|
881
|
+
function handleRaycast(obj) {
|
|
882
|
+
const state = getRootState(obj);
|
|
883
|
+
// Skip event handling when noEvents is set, or when the raycasters camera is null
|
|
884
|
+
if (!state || !state.events.enabled || state.raycaster.camera === null) return [];
|
|
878
885
|
|
|
879
|
-
//
|
|
880
|
-
if (
|
|
886
|
+
// When the camera is undefined we have to call the event layers update function
|
|
887
|
+
if (state.raycaster.camera === undefined) {
|
|
888
|
+
var _state$previousRoot;
|
|
889
|
+
state.events.compute == null ? void 0 : state.events.compute(event, state, (_state$previousRoot = state.previousRoot) == null ? void 0 : _state$previousRoot.getState());
|
|
890
|
+
// If the camera is still undefined we have to skip this layer entirely
|
|
891
|
+
if (state.raycaster.camera === undefined) state.raycaster.camera = null;
|
|
892
|
+
}
|
|
881
893
|
|
|
882
|
-
//
|
|
883
|
-
|
|
884
|
-
instance = prepare(new target(...args), {
|
|
885
|
-
type,
|
|
886
|
-
root,
|
|
887
|
-
attach,
|
|
888
|
-
// Save args in case we need to reconstruct later for HMR
|
|
889
|
-
memoizedProps: {
|
|
890
|
-
args
|
|
891
|
-
}
|
|
892
|
-
});
|
|
894
|
+
// Intersect object by object
|
|
895
|
+
return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : [];
|
|
893
896
|
}
|
|
894
897
|
|
|
895
|
-
//
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
898
|
+
// Collect events
|
|
899
|
+
let hits = eventsObjects
|
|
900
|
+
// Intersect objects
|
|
901
|
+
.flatMap(handleRaycast)
|
|
902
|
+
// Sort by event priority and distance
|
|
903
|
+
.sort((a, b) => {
|
|
904
|
+
const aState = getRootState(a.object);
|
|
905
|
+
const bState = getRootState(b.object);
|
|
906
|
+
if (!aState || !bState) return a.distance - b.distance;
|
|
907
|
+
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
908
|
+
})
|
|
909
|
+
// Filter out duplicates
|
|
910
|
+
.filter(item => {
|
|
911
|
+
const id = makeId(item);
|
|
912
|
+
if (duplicates.has(id)) return false;
|
|
913
|
+
duplicates.add(id);
|
|
914
|
+
return true;
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
// https://github.com/mrdoob/three.js/issues/16031
|
|
918
|
+
// Allow custom userland intersect sort order, this likely only makes sense on the root filter
|
|
919
|
+
if (state.events.filter) hits = state.events.filter(hits, state);
|
|
899
920
|
|
|
900
|
-
//
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
// The attach attribute implies that the object attaches itself on the parent
|
|
912
|
-
if ((_child$__r3f = child.__r3f) != null && _child$__r3f.attach) {
|
|
913
|
-
attach(parentInstance, child, child.__r3f.attach);
|
|
914
|
-
} else if (child.isObject3D && parentInstance.isObject3D) {
|
|
915
|
-
// add in the usual parent-child way
|
|
916
|
-
parentInstance.add(child);
|
|
917
|
-
added = true;
|
|
921
|
+
// Bubble up the events, find the event source (eventObject)
|
|
922
|
+
for (const hit of hits) {
|
|
923
|
+
let eventObject = hit.object;
|
|
924
|
+
// Bubble event up
|
|
925
|
+
while (eventObject) {
|
|
926
|
+
var _r3f2;
|
|
927
|
+
if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({
|
|
928
|
+
...hit,
|
|
929
|
+
eventObject
|
|
930
|
+
});
|
|
931
|
+
eventObject = eventObject.parent;
|
|
918
932
|
}
|
|
919
|
-
// This is for anything that used attach, and for non-Object3Ds that don't get attached to props;
|
|
920
|
-
// that is, anything that's a child in React but not a child in the scenegraph.
|
|
921
|
-
if (!added) (_parentInstance$__r3f = parentInstance.__r3f) == null ? void 0 : _parentInstance$__r3f.objects.push(child);
|
|
922
|
-
if (!child.__r3f) prepare(child, {});
|
|
923
|
-
child.__r3f.parent = parentInstance;
|
|
924
|
-
updateInstance(child);
|
|
925
|
-
invalidateInstance(child);
|
|
926
933
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
if ((_child$__r3f2 = child.__r3f) != null && _child$__r3f2.attach) {
|
|
933
|
-
attach(parentInstance, child, child.__r3f.attach);
|
|
934
|
-
} else if (child.isObject3D && parentInstance.isObject3D) {
|
|
935
|
-
child.parent = parentInstance;
|
|
936
|
-
child.dispatchEvent({
|
|
937
|
-
type: 'added'
|
|
938
|
-
});
|
|
939
|
-
const restSiblings = parentInstance.children.filter(sibling => sibling !== child);
|
|
940
|
-
const index = restSiblings.indexOf(beforeChild);
|
|
941
|
-
parentInstance.children = [...restSiblings.slice(0, index), child, ...restSiblings.slice(index)];
|
|
942
|
-
added = true;
|
|
934
|
+
|
|
935
|
+
// If the interaction is captured, make all capturing targets part of the intersect.
|
|
936
|
+
if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
|
|
937
|
+
for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
|
|
938
|
+
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
943
939
|
}
|
|
944
|
-
if (!added) (_parentInstance$__r3f2 = parentInstance.__r3f) == null ? void 0 : _parentInstance$__r3f2.objects.push(child);
|
|
945
|
-
if (!child.__r3f) prepare(child, {});
|
|
946
|
-
child.__r3f.parent = parentInstance;
|
|
947
|
-
updateInstance(child);
|
|
948
|
-
invalidateInstance(child);
|
|
949
940
|
}
|
|
941
|
+
return intersections;
|
|
950
942
|
}
|
|
951
|
-
function removeRecursive(array, parent, dispose = false) {
|
|
952
|
-
if (array) [...array].forEach(child => removeChild(parent, child, dispose));
|
|
953
|
-
}
|
|
954
|
-
function removeChild(parentInstance, child, dispose) {
|
|
955
|
-
if (child) {
|
|
956
|
-
var _parentInstance$__r3f3, _child$__r3f3, _child$__r3f5;
|
|
957
|
-
// Clear the parent reference
|
|
958
|
-
if (child.__r3f) child.__r3f.parent = null;
|
|
959
|
-
// Remove child from the parents objects
|
|
960
|
-
if ((_parentInstance$__r3f3 = parentInstance.__r3f) != null && _parentInstance$__r3f3.objects) parentInstance.__r3f.objects = parentInstance.__r3f.objects.filter(x => x !== child);
|
|
961
|
-
// Remove attachment
|
|
962
|
-
if ((_child$__r3f3 = child.__r3f) != null && _child$__r3f3.attach) {
|
|
963
|
-
detach(parentInstance, child, child.__r3f.attach);
|
|
964
|
-
} else if (child.isObject3D && parentInstance.isObject3D) {
|
|
965
|
-
var _child$__r3f4;
|
|
966
|
-
parentInstance.remove(child);
|
|
967
|
-
// Remove interactivity
|
|
968
|
-
if ((_child$__r3f4 = child.__r3f) != null && _child$__r3f4.root) {
|
|
969
|
-
removeInteractivity(child.__r3f.root, child);
|
|
970
|
-
}
|
|
971
|
-
}
|
|
972
943
|
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
// - a dispose method,
|
|
977
|
-
// - it cannot be a <primitive object={...} />
|
|
978
|
-
// - it cannot be a THREE.Scene, because three has broken it's own api
|
|
979
|
-
//
|
|
980
|
-
// Since disposal is recursive, we can check the optional dispose arg, which will be undefined
|
|
981
|
-
// when the reconciler calls it, but then carry our own check recursively
|
|
982
|
-
const isPrimitive = (_child$__r3f5 = child.__r3f) == null ? void 0 : _child$__r3f5.primitive;
|
|
983
|
-
const shouldDispose = dispose === undefined ? child.dispose !== null && !isPrimitive : dispose;
|
|
944
|
+
/** Handles intersections by forwarding them to handlers */
|
|
945
|
+
function handleIntersects(intersections, event, delta, callback) {
|
|
946
|
+
const rootState = store.getState();
|
|
984
947
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
948
|
+
// If anything has been found, forward it to the event listeners
|
|
949
|
+
if (intersections.length) {
|
|
950
|
+
const localState = {
|
|
951
|
+
stopped: false
|
|
952
|
+
};
|
|
953
|
+
for (const hit of intersections) {
|
|
954
|
+
const state = getRootState(hit.object) || rootState;
|
|
955
|
+
const {
|
|
956
|
+
raycaster,
|
|
957
|
+
pointer,
|
|
958
|
+
camera,
|
|
959
|
+
internal
|
|
960
|
+
} = state;
|
|
961
|
+
const unprojectedPoint = new THREE__namespace.Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
962
|
+
const hasPointerCapture = id => {
|
|
963
|
+
var _internal$capturedMap, _internal$capturedMap2;
|
|
964
|
+
return (_internal$capturedMap = (_internal$capturedMap2 = internal.capturedMap.get(id)) == null ? void 0 : _internal$capturedMap2.has(hit.eventObject)) != null ? _internal$capturedMap : false;
|
|
965
|
+
};
|
|
966
|
+
const setPointerCapture = id => {
|
|
967
|
+
const captureData = {
|
|
968
|
+
intersection: hit,
|
|
969
|
+
target: event.target
|
|
970
|
+
};
|
|
971
|
+
if (internal.capturedMap.has(id)) {
|
|
972
|
+
// if the pointerId was previously captured, we add the hit to the
|
|
973
|
+
// event capturedMap.
|
|
974
|
+
internal.capturedMap.get(id).set(hit.eventObject, captureData);
|
|
975
|
+
} else {
|
|
976
|
+
// if the pointerId was not previously captured, we create a map
|
|
977
|
+
// containing the hitObject, and the hit. hitObject is used for
|
|
978
|
+
// faster access.
|
|
979
|
+
internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
|
|
980
|
+
}
|
|
981
|
+
event.target.setPointerCapture(id);
|
|
982
|
+
};
|
|
983
|
+
const releasePointerCapture = id => {
|
|
984
|
+
const captures = internal.capturedMap.get(id);
|
|
985
|
+
if (captures) {
|
|
986
|
+
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
987
|
+
}
|
|
988
|
+
};
|
|
992
989
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
990
|
+
// Add native event props
|
|
991
|
+
let extractEventProps = {};
|
|
992
|
+
// This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
|
|
993
|
+
for (let prop in event) {
|
|
994
|
+
let property = event[prop];
|
|
995
|
+
// Only copy over atomics, leave functions alone as these should be
|
|
996
|
+
// called as event.nativeEvent.fn()
|
|
997
|
+
if (typeof property !== 'function') extractEventProps[prop] = property;
|
|
998
|
+
}
|
|
999
|
+
let raycastEvent = {
|
|
1000
|
+
...hit,
|
|
1001
|
+
...extractEventProps,
|
|
1002
|
+
pointer,
|
|
1003
|
+
intersections,
|
|
1004
|
+
stopped: localState.stopped,
|
|
1005
|
+
delta,
|
|
1006
|
+
unprojectedPoint,
|
|
1007
|
+
ray: raycaster.ray,
|
|
1008
|
+
camera: camera,
|
|
1009
|
+
// Hijack stopPropagation, which just sets a flag
|
|
1010
|
+
stopPropagation() {
|
|
1011
|
+
// https://github.com/pmndrs/react-three-fiber/issues/596
|
|
1012
|
+
// Events are not allowed to stop propagation if the pointer has been captured
|
|
1013
|
+
const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId);
|
|
1001
1014
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1015
|
+
// We only authorize stopPropagation...
|
|
1016
|
+
if (
|
|
1017
|
+
// ...if this pointer hasn't been captured
|
|
1018
|
+
!capturesForPointer ||
|
|
1019
|
+
// ... or if the hit object is capturing the pointer
|
|
1020
|
+
capturesForPointer.has(hit.eventObject)) {
|
|
1021
|
+
raycastEvent.stopped = localState.stopped = true;
|
|
1022
|
+
// Propagation is stopped, remove all other hover records
|
|
1023
|
+
// An event handler is only allowed to flush other handlers if it is hovered itself
|
|
1024
|
+
if (internal.hovered.size && Array.from(internal.hovered.values()).find(i => i.eventObject === hit.eventObject)) {
|
|
1025
|
+
// Objects cannot flush out higher up objects that have already caught the event
|
|
1026
|
+
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
1027
|
+
cancelPointer([...higher, hit]);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
},
|
|
1031
|
+
// there should be a distinction between target and currentTarget
|
|
1032
|
+
target: {
|
|
1033
|
+
hasPointerCapture,
|
|
1034
|
+
setPointerCapture,
|
|
1035
|
+
releasePointerCapture
|
|
1036
|
+
},
|
|
1037
|
+
currentTarget: {
|
|
1038
|
+
hasPointerCapture,
|
|
1039
|
+
setPointerCapture,
|
|
1040
|
+
releasePointerCapture
|
|
1041
|
+
},
|
|
1042
|
+
nativeEvent: event
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
// Call subscribers
|
|
1046
|
+
callback(raycastEvent);
|
|
1047
|
+
// Event bubbling may be interrupted by stopPropagation
|
|
1048
|
+
if (localState.stopped === true) break;
|
|
1011
1049
|
}
|
|
1012
|
-
invalidateInstance(parentInstance);
|
|
1013
1050
|
}
|
|
1051
|
+
return intersections;
|
|
1014
1052
|
}
|
|
1015
|
-
function
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1053
|
+
function cancelPointer(intersections) {
|
|
1054
|
+
const {
|
|
1055
|
+
internal
|
|
1056
|
+
} = store.getState();
|
|
1057
|
+
for (const hoveredObj of internal.hovered.values()) {
|
|
1058
|
+
// When no objects were hit or the the hovered object wasn't found underneath the cursor
|
|
1059
|
+
// we call onPointerOut and delete the object from the hovered-elements map
|
|
1060
|
+
if (!intersections.length || !intersections.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
|
|
1061
|
+
const eventObject = hoveredObj.eventObject;
|
|
1062
|
+
const instance = eventObject.__r3f;
|
|
1063
|
+
const handlers = instance == null ? void 0 : instance.handlers;
|
|
1064
|
+
internal.hovered.delete(makeId(hoveredObj));
|
|
1065
|
+
if (instance != null && instance.eventCount) {
|
|
1066
|
+
// Clear out intersects, they are outdated by now
|
|
1067
|
+
const data = {
|
|
1068
|
+
...hoveredObj,
|
|
1069
|
+
intersections
|
|
1070
|
+
};
|
|
1071
|
+
handlers.onPointerOut == null ? void 0 : handlers.onPointerOut(data);
|
|
1072
|
+
handlers.onPointerLeave == null ? void 0 : handlers.onPointerLeave(data);
|
|
1073
|
+
}
|
|
1027
1074
|
}
|
|
1028
|
-
instance.children = instance.children.filter(child => !child.__r3f);
|
|
1029
1075
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1076
|
+
}
|
|
1077
|
+
function pointerMissed(event, objects) {
|
|
1078
|
+
for (let i = 0; i < objects.length; i++) {
|
|
1079
|
+
const instance = objects[i].__r3f;
|
|
1080
|
+
instance == null ? void 0 : instance.handlers.onPointerMissed == null ? void 0 : instance.handlers.onPointerMissed(event);
|
|
1034
1081
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1082
|
+
}
|
|
1083
|
+
function handlePointer(name) {
|
|
1084
|
+
// Deal with cancelation
|
|
1085
|
+
switch (name) {
|
|
1086
|
+
case 'onPointerLeave':
|
|
1087
|
+
case 'onPointerCancel':
|
|
1088
|
+
return () => cancelPointer([]);
|
|
1089
|
+
case 'onLostPointerCapture':
|
|
1090
|
+
return event => {
|
|
1091
|
+
const {
|
|
1092
|
+
internal
|
|
1093
|
+
} = store.getState();
|
|
1094
|
+
if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
|
|
1095
|
+
// If the object event interface had onLostPointerCapture, we'd call it here on every
|
|
1096
|
+
// object that's getting removed. We call it on the next frame because onLostPointerCapture
|
|
1097
|
+
// fires before onPointerUp. Otherwise pointerUp would never be called if the event didn't
|
|
1098
|
+
// happen in the object it originated from, leaving components in a in-between state.
|
|
1099
|
+
requestAnimationFrame(() => {
|
|
1100
|
+
// Only release if pointer-up didn't do it already
|
|
1101
|
+
if (internal.capturedMap.has(event.pointerId)) {
|
|
1102
|
+
internal.capturedMap.delete(event.pointerId);
|
|
1103
|
+
cancelPointer([]);
|
|
1104
|
+
}
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1037
1108
|
}
|
|
1038
|
-
appendChild(parent, newInstance);
|
|
1039
1109
|
|
|
1040
|
-
//
|
|
1041
|
-
|
|
1042
|
-
const
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
if (fiber !== null) {
|
|
1047
|
-
fiber.stateNode = newInstance;
|
|
1048
|
-
if (fiber.ref) {
|
|
1049
|
-
if (typeof fiber.ref === 'function') fiber.ref(newInstance);else fiber.ref.current = newInstance;
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
}
|
|
1110
|
+
// Any other pointer goes here ...
|
|
1111
|
+
return function handleEvent(event) {
|
|
1112
|
+
const {
|
|
1113
|
+
onPointerMissed,
|
|
1114
|
+
internal
|
|
1115
|
+
} = store.getState();
|
|
1054
1116
|
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
const reconciler = Reconciler__default["default"]({
|
|
1058
|
-
createInstance,
|
|
1059
|
-
removeChild,
|
|
1060
|
-
appendChild,
|
|
1061
|
-
appendInitialChild: appendChild,
|
|
1062
|
-
insertBefore,
|
|
1063
|
-
supportsMutation: true,
|
|
1064
|
-
isPrimaryRenderer: false,
|
|
1065
|
-
supportsPersistence: false,
|
|
1066
|
-
supportsHydration: false,
|
|
1067
|
-
noTimeout: -1,
|
|
1068
|
-
appendChildToContainer: (container, child) => {
|
|
1069
|
-
if (!child) return;
|
|
1117
|
+
// prepareRay(event)
|
|
1118
|
+
internal.lastEvent.current = event;
|
|
1070
1119
|
|
|
1071
|
-
//
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1120
|
+
// Get fresh intersects
|
|
1121
|
+
const isPointerMove = name === 'onPointerMove';
|
|
1122
|
+
const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
|
|
1123
|
+
const filter = isPointerMove ? filterPointerEvents : undefined;
|
|
1124
|
+
const hits = intersect(event, filter);
|
|
1125
|
+
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
1074
1126
|
|
|
1075
|
-
//
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
if (!child) return;
|
|
1081
|
-
removeChild(container.getState().scene, child);
|
|
1082
|
-
},
|
|
1083
|
-
insertInContainerBefore: (container, child, beforeChild) => {
|
|
1084
|
-
if (!child || !beforeChild) return;
|
|
1127
|
+
// Save initial coordinates on pointer-down
|
|
1128
|
+
if (name === 'onPointerDown') {
|
|
1129
|
+
internal.initialClick = [event.offsetX, event.offsetY];
|
|
1130
|
+
internal.initialHits = hits.map(hit => hit.eventObject);
|
|
1131
|
+
}
|
|
1085
1132
|
|
|
1086
|
-
//
|
|
1087
|
-
|
|
1088
|
-
if (!
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
prepareUpdate(instance, _type, oldProps, newProps) {
|
|
1101
|
-
// Create diff-sets
|
|
1102
|
-
if (instance.__r3f.primitive && newProps.object && newProps.object !== instance) {
|
|
1103
|
-
return [true];
|
|
1104
|
-
} else {
|
|
1105
|
-
// This is a data object, let's extract critical information about it
|
|
1106
|
-
const {
|
|
1107
|
-
args: argsNew = [],
|
|
1108
|
-
children: cN,
|
|
1109
|
-
...restNew
|
|
1110
|
-
} = newProps;
|
|
1111
|
-
const {
|
|
1112
|
-
args: argsOld = [],
|
|
1113
|
-
children: cO,
|
|
1114
|
-
...restOld
|
|
1115
|
-
} = oldProps;
|
|
1133
|
+
// If a click yields no results, pass it back to the user as a miss
|
|
1134
|
+
// Missed events have to come first in order to establish user-land side-effect clean up
|
|
1135
|
+
if (isClickEvent && !hits.length) {
|
|
1136
|
+
if (delta <= 2) {
|
|
1137
|
+
pointerMissed(event, internal.interaction);
|
|
1138
|
+
if (onPointerMissed) onPointerMissed(event);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
// Take care of unhover
|
|
1142
|
+
if (isPointerMove) cancelPointer(hits);
|
|
1143
|
+
function onIntersect(data) {
|
|
1144
|
+
const eventObject = data.eventObject;
|
|
1145
|
+
const instance = eventObject.__r3f;
|
|
1146
|
+
const handlers = instance == null ? void 0 : instance.handlers;
|
|
1116
1147
|
|
|
1117
|
-
//
|
|
1118
|
-
if (!
|
|
1148
|
+
// Check presence of handlers
|
|
1149
|
+
if (!(instance != null && instance.eventCount)) return;
|
|
1119
1150
|
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1151
|
+
/*
|
|
1152
|
+
MAYBE TODO, DELETE IF NOT:
|
|
1153
|
+
Check if the object is captured, captured events should not have intersects running in parallel
|
|
1154
|
+
But wouldn't it be better to just replace capturedMap with a single entry?
|
|
1155
|
+
Also, are we OK with straight up making picking up multiple objects impossible?
|
|
1156
|
+
|
|
1157
|
+
const pointerId = (data as ThreeEvent<PointerEvent>).pointerId
|
|
1158
|
+
if (pointerId !== undefined) {
|
|
1159
|
+
const capturedMeshSet = internal.capturedMap.get(pointerId)
|
|
1160
|
+
if (capturedMeshSet) {
|
|
1161
|
+
const captured = capturedMeshSet.get(eventObject)
|
|
1162
|
+
if (captured && captured.localState.stopped) return
|
|
1163
|
+
}
|
|
1164
|
+
}*/
|
|
1125
1165
|
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1166
|
+
if (isPointerMove) {
|
|
1167
|
+
// Move event ...
|
|
1168
|
+
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
1169
|
+
// When enter or out is present take care of hover-state
|
|
1170
|
+
const id = makeId(data);
|
|
1171
|
+
const hoveredItem = internal.hovered.get(id);
|
|
1172
|
+
if (!hoveredItem) {
|
|
1173
|
+
// If the object wasn't previously hovered, book it and call its handler
|
|
1174
|
+
internal.hovered.set(id, data);
|
|
1175
|
+
handlers.onPointerOver == null ? void 0 : handlers.onPointerOver(data);
|
|
1176
|
+
handlers.onPointerEnter == null ? void 0 : handlers.onPointerEnter(data);
|
|
1177
|
+
} else if (hoveredItem.stopped) {
|
|
1178
|
+
// If the object was previously hovered and stopped, we shouldn't allow other items to proceed
|
|
1179
|
+
data.stopPropagation();
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
// Call mouse move
|
|
1183
|
+
handlers.onPointerMove == null ? void 0 : handlers.onPointerMove(data);
|
|
1184
|
+
} else {
|
|
1185
|
+
// All other events ...
|
|
1186
|
+
const handler = handlers[name];
|
|
1187
|
+
if (handler) {
|
|
1188
|
+
// Forward all events back to their respective handlers with the exception of click events,
|
|
1189
|
+
// which must use the initial target
|
|
1190
|
+
if (!isClickEvent || internal.initialHits.includes(eventObject)) {
|
|
1191
|
+
// Missed events have to come first
|
|
1192
|
+
pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
|
|
1193
|
+
// Now call the handler
|
|
1194
|
+
handler(data);
|
|
1195
|
+
}
|
|
1196
|
+
} else {
|
|
1197
|
+
// Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
|
|
1198
|
+
if (isClickEvent && internal.initialHits.includes(eventObject)) {
|
|
1199
|
+
pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1143
1203
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
preparePortalMount: container => prepare(container.getState().scene),
|
|
1148
|
-
resetAfterCommit: () => {},
|
|
1149
|
-
shouldSetTextContent: () => false,
|
|
1150
|
-
clearContainer: () => false,
|
|
1151
|
-
hideInstance(instance) {
|
|
1152
|
-
var _instance$__r3f4;
|
|
1153
|
-
// Detach while the instance is hidden
|
|
1154
|
-
const {
|
|
1155
|
-
attach: type,
|
|
1156
|
-
parent
|
|
1157
|
-
} = (_instance$__r3f4 = instance.__r3f) != null ? _instance$__r3f4 : {};
|
|
1158
|
-
if (type && parent) detach(parent, instance, type);
|
|
1159
|
-
if (instance.isObject3D) instance.visible = false;
|
|
1160
|
-
invalidateInstance(instance);
|
|
1161
|
-
},
|
|
1162
|
-
unhideInstance(instance, props) {
|
|
1163
|
-
var _instance$__r3f5;
|
|
1164
|
-
// Re-attach when the instance is unhidden
|
|
1165
|
-
const {
|
|
1166
|
-
attach: type,
|
|
1167
|
-
parent
|
|
1168
|
-
} = (_instance$__r3f5 = instance.__r3f) != null ? _instance$__r3f5 : {};
|
|
1169
|
-
if (type && parent) attach(parent, instance, type);
|
|
1170
|
-
if (instance.isObject3D && props.visible == null || props.visible) instance.visible = true;
|
|
1171
|
-
invalidateInstance(instance);
|
|
1172
|
-
},
|
|
1173
|
-
createTextInstance: handleTextInstance,
|
|
1174
|
-
hideTextInstance: handleTextInstance,
|
|
1175
|
-
unhideTextInstance: handleTextInstance,
|
|
1176
|
-
// https://github.com/pmndrs/react-three-fiber/pull/2360#discussion_r916356874
|
|
1177
|
-
// @ts-ignore
|
|
1178
|
-
getCurrentEventPriority: () => _getEventPriority ? _getEventPriority() : constants.DefaultEventPriority,
|
|
1179
|
-
beforeActiveInstanceBlur: () => {},
|
|
1180
|
-
afterActiveInstanceBlur: () => {},
|
|
1181
|
-
detachDeletedInstance: () => {},
|
|
1182
|
-
now: typeof performance !== 'undefined' && is.fun(performance.now) ? performance.now : is.fun(Date.now) ? Date.now : () => 0,
|
|
1183
|
-
// https://github.com/pmndrs/react-three-fiber/pull/2360#discussion_r920883503
|
|
1184
|
-
scheduleTimeout: is.fun(setTimeout) ? setTimeout : undefined,
|
|
1185
|
-
cancelTimeout: is.fun(clearTimeout) ? clearTimeout : undefined
|
|
1186
|
-
});
|
|
1204
|
+
handleIntersects(hits, event, delta, onIntersect);
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1187
1207
|
return {
|
|
1188
|
-
|
|
1189
|
-
applyProps: applyProps$1
|
|
1208
|
+
handlePointer
|
|
1190
1209
|
};
|
|
1191
1210
|
}
|
|
1192
1211
|
|
|
@@ -1874,6 +1893,7 @@ function createRoot(canvas) {
|
|
|
1874
1893
|
|
|
1875
1894
|
// Safely set color management if available.
|
|
1876
1895
|
// Avoid accessing THREE.ColorManagement to play nice with older versions
|
|
1896
|
+
const ColorManagement = getColorManagement();
|
|
1877
1897
|
if (ColorManagement) {
|
|
1878
1898
|
if ('enabled' in ColorManagement) ColorManagement.enabled = !legacy;else if ('legacyMode' in ColorManagement) ColorManagement.legacyMode = legacy;
|
|
1879
1899
|
}
|