@react-three/fiber 8.0.0-beta.5 → 8.0.0-beta.8

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.
@@ -15,6 +15,15 @@ const isDiffSet = def => def && !!def.memoized && !!def.changes;
15
15
  function calculateDpr(dpr) {
16
16
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
17
17
  }
18
+ /**
19
+ * Returns instance root state
20
+ */
21
+
22
+ const getRootState = obj => {
23
+ var _r3f;
24
+
25
+ return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
26
+ };
18
27
  /**
19
28
  * Picks or omits keys from an object
20
29
  * `omit` will filter out keys, and otherwise cherry-pick them.
@@ -24,11 +33,7 @@ function filterKeys(obj, omit, ...keys) {
24
33
  const keysToSelect = new Set(keys);
25
34
  return Object.entries(obj).reduce((acc, [key, value]) => {
26
35
  const shouldInclude = !omit;
27
-
28
- if (keysToSelect.has(key) === shouldInclude) {
29
- acc[key] = value;
30
- }
31
-
36
+ if (keysToSelect.has(key) === shouldInclude) acc[key] = value;
32
37
  return acc;
33
38
  }, {});
34
39
  }
@@ -382,6 +387,7 @@ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
382
387
 
383
388
  function removeInteractivity(store, object) {
384
389
  const {
390
+ events,
385
391
  internal
386
392
  } = store.getState(); // Removes every trace of an object from the data store
387
393
 
@@ -389,6 +395,7 @@ function removeInteractivity(store, object) {
389
395
  internal.initialHits = internal.initialHits.filter(o => o !== object);
390
396
  internal.hovered.forEach((value, key) => {
391
397
  if (value.eventObject === object || value.object === object) {
398
+ // Clear out intersects, they are outdated by now
392
399
  internal.hovered.delete(key);
393
400
  }
394
401
  });
@@ -398,31 +405,8 @@ function removeInteractivity(store, object) {
398
405
  }
399
406
  function createEvents(store) {
400
407
  const temp = new THREE.Vector3();
401
- /** Sets up defaultRaycaster */
402
-
403
- function prepareRay(event) {
404
- var _customOffsets$offset, _customOffsets$offset2, _customOffsets$width, _customOffsets$height;
405
-
406
- const state = store.getState();
407
- const {
408
- raycaster,
409
- mouse,
410
- camera,
411
- size
412
- } = state; // https://github.com/pmndrs/react-three-fiber/pull/782
413
- // Events trigger outside of canvas when moved
414
-
415
- const customOffsets = raycaster.computeOffsets == null ? void 0 : raycaster.computeOffsets(event, state);
416
- const offsetX = (_customOffsets$offset = customOffsets == null ? void 0 : customOffsets.offsetX) != null ? _customOffsets$offset : event.offsetX;
417
- const offsetY = (_customOffsets$offset2 = customOffsets == null ? void 0 : customOffsets.offsetY) != null ? _customOffsets$offset2 : event.offsetY;
418
- const width = (_customOffsets$width = customOffsets == null ? void 0 : customOffsets.width) != null ? _customOffsets$width : size.width;
419
- const height = (_customOffsets$height = customOffsets == null ? void 0 : customOffsets.height) != null ? _customOffsets$height : size.height;
420
- mouse.set(offsetX / width * 2 - 1, -(offsetY / height) * 2 + 1);
421
- raycaster.setFromCamera(mouse, camera);
422
- }
423
408
  /** Calculates delta */
424
409
 
425
-
426
410
  function calculateDistance(event) {
427
411
  const {
428
412
  internal
@@ -442,55 +426,70 @@ function createEvents(store) {
442
426
  }));
443
427
  }
444
428
 
445
- function intersect(filter) {
429
+ function intersect(event, filter) {
446
430
  const state = store.getState();
447
- const {
448
- raycaster,
449
- internal
450
- } = state; // Skip event handling when noEvents is set
451
-
452
- if (!raycaster.enabled) return [];
453
- const seen = new Set();
431
+ const duplicates = new Set();
454
432
  const intersections = []; // Allow callers to eliminate event objects
455
433
 
456
- const eventsObjects = filter ? filter(internal.interaction) : internal.interaction; // Intersect known handler objects and filter against duplicates
434
+ const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction; // Reset all raycaster cameras to undefined
435
+
436
+ eventsObjects.forEach(obj => {
437
+ const state = getRootState(obj);
438
+
439
+ if (state) {
440
+ state.raycaster.camera = undefined;
441
+ }
442
+ }); // Collect events
443
+
444
+ let hits = eventsObjects // Intersect objects
445
+ .flatMap(obj => {
446
+ const state = getRootState(obj); // Skip event handling when noEvents is set, or when the raycasters camera is null
447
+
448
+ if (!state || !state.events.enabled || state.raycaster.camera === null) return []; // When the camera is undefined we have to call the event layers update function
449
+
450
+ if (state.raycaster.camera === undefined) {
451
+ var _state$previousRoot;
452
+
453
+ state.events.compute == null ? void 0 : state.events.compute(event, state, (_state$previousRoot = state.previousRoot) == null ? void 0 : _state$previousRoot.getState()); // If the camera is still undefined we have to skip this layer entirely
454
+
455
+ if (state.raycaster.camera === undefined) state.raycaster.camera = null;
456
+ } // Intersect object by object
457
457
 
458
- let intersects = raycaster.intersectObjects(eventsObjects, true).filter(item => {
458
+
459
+ return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : [];
460
+ }) // Sort by event priority and distance
461
+ .sort((a, b) => {
462
+ const aState = getRootState(a.object);
463
+ const bState = getRootState(b.object);
464
+ if (!aState || !bState) return 0;
465
+ return bState.events.priority - aState.events.priority || a.distance - b.distance;
466
+ }) // Filter out duplicates
467
+ .filter(item => {
459
468
  const id = makeId(item);
460
- if (seen.has(id)) return false;
461
- seen.add(id);
469
+ if (duplicates.has(id)) return false;
470
+ duplicates.add(id);
462
471
  return true;
463
472
  }); // https://github.com/mrdoob/three.js/issues/16031
464
- // Allow custom userland intersect sort order
473
+ // Allow custom userland intersect sort order, this likely only makes sense on the root filter
465
474
 
466
- if (raycaster.filter) intersects = raycaster.filter(intersects, state);
475
+ if (state.events.filter) hits = state.events.filter(hits, state); // Bubble up the events, find the event source (eventObject)
467
476
 
468
- for (const intersect of intersects) {
469
- let eventObject = intersect.object; // Bubble event up
477
+ for (const hit of hits) {
478
+ let eventObject = hit.object; // Bubble event up
470
479
 
471
480
  while (eventObject) {
472
481
  var _r3f2;
473
482
 
474
- if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...intersect,
483
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...hit,
475
484
  eventObject
476
485
  });
477
486
  eventObject = eventObject.parent;
478
487
  }
479
- }
480
-
481
- return intersections;
482
- }
483
- /** Creates filtered intersects and returns an array of positive hits */
488
+ } // If the interaction is captured, make all capturing targets part of the intersect.
484
489
 
485
490
 
486
- function patchIntersects(intersections, event) {
487
- const {
488
- internal
489
- } = store.getState(); // If the interaction is captured, make all capturing targets part of the
490
- // intersect.
491
-
492
- if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
493
- for (let captureData of internal.capturedMap.get(event.pointerId).values()) {
491
+ if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
492
+ for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
494
493
  intersections.push(captureData.intersection);
495
494
  }
496
495
  }
@@ -503,13 +502,13 @@ function createEvents(store) {
503
502
  function handleIntersects(intersections, event, delta, callback) {
504
503
  const {
505
504
  raycaster,
506
- mouse,
505
+ pointer,
507
506
  camera,
508
507
  internal
509
508
  } = store.getState(); // If anything has been found, forward it to the event listeners
510
509
 
511
510
  if (intersections.length) {
512
- const unprojectedPoint = temp.set(mouse.x, mouse.y, 0).unproject(camera);
511
+ const unprojectedPoint = temp.set(pointer.x, pointer.y, 0).unproject(camera);
513
512
  const localState = {
514
513
  stopped: false
515
514
  };
@@ -560,8 +559,7 @@ function createEvents(store) {
560
559
 
561
560
  let raycastEvent = { ...hit,
562
561
  ...extractEventProps,
563
- spaceX: mouse.x,
564
- spaceY: mouse.y,
562
+ pointer,
565
563
  intersections,
566
564
  stopped: localState.stopped,
567
565
  delta,
@@ -598,8 +596,6 @@ function createEvents(store) {
598
596
  setPointerCapture,
599
597
  releasePointerCapture
600
598
  },
601
- sourceEvent: event,
602
- // deprecated
603
599
  nativeEvent: event
604
600
  }; // Call subscribers
605
601
 
@@ -664,14 +660,15 @@ function createEvents(store) {
664
660
  const {
665
661
  onPointerMissed,
666
662
  internal
667
- } = store.getState();
668
- prepareRay(event);
663
+ } = store.getState(); //prepareRay(event)
664
+
669
665
  internal.lastEvent.current = event; // Get fresh intersects
670
666
 
671
667
  const isPointerMove = name === 'onPointerMove';
672
668
  const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
673
- const filter = isPointerMove ? filterPointerEvents : undefined;
674
- const hits = patchIntersects(intersect(filter), event);
669
+ const filter = isPointerMove ? filterPointerEvents : undefined; //const hits = patchIntersects(intersect(filter), event)
670
+
671
+ const hits = intersect(event, filter);
675
672
  const delta = isClickEvent ? calculateDistance(event) : 0; // Save initial coordinates on pointer-down
676
673
 
677
674
  if (name === 'onPointerDown') {
@@ -1150,17 +1147,20 @@ const createStore = (invalidate, advance) => {
1150
1147
  }
1151
1148
  }));
1152
1149
 
1150
+ const pointer = new THREE.Vector2();
1153
1151
  return {
1152
+ set,
1153
+ get,
1154
1154
  // Mock objects that have to be configured
1155
1155
  gl: null,
1156
1156
  camera: null,
1157
1157
  raycaster: null,
1158
1158
  events: {
1159
+ priority: 1,
1160
+ enabled: true,
1159
1161
  connected: false
1160
1162
  },
1161
1163
  xr: null,
1162
- set,
1163
- get,
1164
1164
  invalidate: () => invalidate(get()),
1165
1165
  advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1166
1166
  linear: false,
@@ -1168,7 +1168,8 @@ const createStore = (invalidate, advance) => {
1168
1168
  scene: prepare(new THREE.Scene()),
1169
1169
  controls: null,
1170
1170
  clock: new THREE.Clock(),
1171
- mouse: new THREE.Vector2(),
1171
+ pointer,
1172
+ mouse: pointer,
1172
1173
  frameloop: 'always',
1173
1174
  onPointerMissed: undefined,
1174
1175
  performance: {
@@ -1200,6 +1201,11 @@ const createStore = (invalidate, advance) => {
1200
1201
  factor: 0,
1201
1202
  getCurrentViewport
1202
1203
  },
1204
+ setEvents: events => set(state => ({ ...state,
1205
+ events: { ...state.events,
1206
+ events
1207
+ }
1208
+ })),
1203
1209
  setSize: (width, height) => {
1204
1210
  const camera = get().camera;
1205
1211
  const size = {
@@ -1237,6 +1243,7 @@ const createStore = (invalidate, advance) => {
1237
1243
  frameloop
1238
1244
  }));
1239
1245
  },
1246
+ previousRoot: undefined,
1240
1247
  internal: {
1241
1248
  active: false,
1242
1249
  priority: 0,
@@ -1438,43 +1445,6 @@ function useStore() {
1438
1445
  function useThree(selector = state => state, equalityFn) {
1439
1446
  return useStore()(selector, equalityFn);
1440
1447
  }
1441
- function useInject(state) {
1442
- const useOriginalStore = useStore();
1443
- const useInjectStore = React.useMemo(() => {
1444
- const useInjected = (sel = state => state) => {
1445
- // Execute the useStore hook with the selector once, to maintain reactivity, result doesn't matter
1446
- useOriginalStore(sel); // Inject data and return the result, either selected or raw
1447
-
1448
- return sel({ ...useOriginalStore.getState(),
1449
- ...state
1450
- });
1451
- };
1452
-
1453
- useInjected.setState = useOriginalStore.setState;
1454
- useInjected.destroy = useOriginalStore.destroy; // Patch getState
1455
-
1456
- useInjected.getState = () => {
1457
- return { ...useOriginalStore.getState(),
1458
- ...state
1459
- };
1460
- }; // Patch subscribe
1461
-
1462
-
1463
- useInjected.subscribe = listener => {
1464
- return useOriginalStore.subscribe((current, previous) => listener({ ...current,
1465
- ...state
1466
- }, previous));
1467
- };
1468
-
1469
- return useInjected;
1470
- }, [useOriginalStore, state]); // Return the patched store and a provider component
1471
-
1472
- return React.useMemo(() => [({
1473
- children
1474
- }) => /*#__PURE__*/React.createElement(context.Provider, {
1475
- value: useInjectStore
1476
- }, children), useInjectStore], [useInjectStore]);
1477
- }
1478
1448
  function useFrame(callback, renderPriority = 0) {
1479
1449
  const subscribe = useStore().getState().internal.subscribe; // Update ref
1480
1450
 
@@ -1501,6 +1471,11 @@ function loadingFn(extensions, onProgress) {
1501
1471
  };
1502
1472
  }
1503
1473
 
1474
+ function useMemoizedFn(fn) {
1475
+ const fnRef = React.useRef(fn);
1476
+ React.useLayoutEffect(() => void (fnRef.current = fn), [fn]);
1477
+ return (...args) => fnRef.current == null ? void 0 : fnRef.current(...args);
1478
+ }
1504
1479
  function useLoader(Proto, input, extensions, onProgress) {
1505
1480
  // Use suspense to load async assets
1506
1481
  const keys = Array.isArray(input) ? input : [input];
@@ -1600,9 +1575,7 @@ function createRoot(canvas) {
1600
1575
  params,
1601
1576
  ...options
1602
1577
  } = raycastOptions || {};
1603
- if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, {
1604
- enabled: true,
1605
- ...options
1578
+ if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options
1606
1579
  });
1607
1580
  if (!is.equ(params, raycaster.params, shallowLoose)) applyProps(raycaster, {
1608
1581
  params: { ...raycaster.params,
@@ -1717,14 +1690,14 @@ function createRoot(canvas) {
1717
1690
  return this;
1718
1691
  },
1719
1692
 
1720
- render(element) {
1693
+ render(children) {
1721
1694
  // The root has to be configured before it can be rendered
1722
1695
  if (!configured) this.configure();
1723
1696
  reconciler.updateContainer( /*#__PURE__*/React.createElement(Provider, {
1724
1697
  store: store,
1725
- element: element,
1698
+ children: children,
1726
1699
  onCreated: onCreated,
1727
- target: canvas
1700
+ rootElement: canvas
1728
1701
  }), fiber, null, () => undefined);
1729
1702
  return store;
1730
1703
  },
@@ -1736,35 +1709,36 @@ function createRoot(canvas) {
1736
1709
  };
1737
1710
  }
1738
1711
 
1739
- function render(element, canvas, config = {}) {
1712
+ function render(children, canvas, config) {
1740
1713
  console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
1741
1714
  const root = createRoot(canvas);
1742
1715
  root.configure(config);
1743
- return root.render(element);
1716
+ return root.render(children);
1744
1717
  }
1745
1718
 
1746
1719
  function Provider({
1747
1720
  store,
1748
- element,
1721
+ children,
1749
1722
  onCreated,
1750
- target
1723
+ rootElement
1751
1724
  }) {
1752
- React.useEffect(() => {
1725
+ React.useLayoutEffect(() => {
1753
1726
  const state = store.getState(); // Flag the canvas active, rendering will now begin
1754
1727
 
1755
1728
  state.set(state => ({
1756
1729
  internal: { ...state.internal,
1757
1730
  active: true
1758
1731
  }
1759
- })); // Connect events
1732
+ })); // Notifiy that init is completed, the scene graph exists, but nothing has yet rendered
1760
1733
 
1761
- state.events.connect == null ? void 0 : state.events.connect(target); // Notifiy that init is completed, the scene graph exists, but nothing has yet rendered
1734
+ if (onCreated) onCreated(state); // Connect events to the targets parent, this is done to ensure events are registered on
1735
+ // a shared target, and not on the canvas itself
1762
1736
 
1763
- if (onCreated) onCreated(state); // eslint-disable-next-line react-hooks/exhaustive-deps
1737
+ if (!store.getState().events.connected) state.events.connect == null ? void 0 : state.events.connect(rootElement); // eslint-disable-next-line react-hooks/exhaustive-deps
1764
1738
  }, []);
1765
1739
  return /*#__PURE__*/React.createElement(context.Provider, {
1766
1740
  value: store
1767
- }, element);
1741
+ }, children);
1768
1742
  }
1769
1743
 
1770
1744
  function unmountComponentAtNode(canvas, callback) {
@@ -1805,20 +1779,58 @@ function createPortal(children, container, state) {
1805
1779
  }
1806
1780
 
1807
1781
  function Portal({
1808
- state,
1782
+ state = {},
1809
1783
  children,
1810
1784
  container
1811
1785
  }) {
1812
1786
  /** This has to be a component because it would not be able to call useThree/useStore otherwise since
1813
- * if this is our environment, then we are in in r3f's renderer but in react-dom, it would trigger
1787
+ * if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
1814
1788
  * the "R3F hooks can only be used within the Canvas component!" warning:
1815
1789
  * <Canvas>
1816
1790
  * {createPortal(...)} */
1817
- const portalState = React.useMemo(() => ({ ...state,
1818
- scene: container
1819
- }), [state, container]);
1820
- const [PortalProvider, portalRoot] = useInject(portalState);
1821
- return /*#__PURE__*/React.createElement(React.Fragment, null, reconciler.createPortal( /*#__PURE__*/React.createElement(PortalProvider, null, children), portalRoot, null));
1791
+ const {
1792
+ events,
1793
+ ...rest
1794
+ } = state;
1795
+ const previousRoot = useStore();
1796
+ const [raycaster] = React.useState(() => new THREE.Raycaster());
1797
+ const inject = React.useCallback((state, injectState) => {
1798
+ const intersect = { ...state
1799
+ };
1800
+
1801
+ if (injectState) {
1802
+ // Only the fields of "state" that do not differ from injectState
1803
+ Object.keys(state).forEach(key => {
1804
+ if ( // Some props should be off-limits
1805
+ !['size', 'viewport', 'internal', 'performance'].includes(key) && // Otherwise filter out the props that are different and let the inject layer take precedence
1806
+ state[key] !== injectState[key]) delete intersect[key];
1807
+ });
1808
+ }
1809
+
1810
+ return { ...intersect,
1811
+ scene: container,
1812
+ previousRoot,
1813
+ raycaster,
1814
+ events: { ...state.events,
1815
+ ...events
1816
+ },
1817
+ ...rest
1818
+ };
1819
+ }, [state]);
1820
+ const [useInjectStore] = React.useState(() => {
1821
+ const store = create((set, get) => ({ ...inject(previousRoot.getState()),
1822
+ set,
1823
+ get
1824
+ }));
1825
+ previousRoot.subscribe(state => useInjectStore.setState(injectState => inject(state, injectState)));
1826
+ return store;
1827
+ });
1828
+ React.useEffect(() => {
1829
+ useInjectStore.setState(injectState => inject(previousRoot.getState(), injectState));
1830
+ }, [inject]);
1831
+ return /*#__PURE__*/React.createElement(React.Fragment, null, reconciler.createPortal( /*#__PURE__*/React.createElement(context.Provider, {
1832
+ value: useInjectStore
1833
+ }, children), useInjectStore, null));
1822
1834
  }
1823
1835
 
1824
1836
  reconciler.injectIntoDevTools({
@@ -1828,4 +1840,4 @@ reconciler.injectIntoDevTools({
1828
1840
  });
1829
1841
  const act = React.unstable_act;
1830
1842
 
1831
- export { createRoot as a, context as b, createEvents as c, createPortal as d, extend as e, reconciler as f, applyProps as g, dispose as h, invalidate as i, advance as j, addEffect as k, addAfterEffect as l, addTail as m, act as n, omit as o, pick as p, roots as q, render as r, useStore as s, threeTypes as t, unmountComponentAtNode as u, useThree as v, useInject as w, useFrame as x, useGraph as y, useLoader as z };
1843
+ export { useLoader as A, createRoot as a, unmountComponentAtNode as b, createEvents as c, context as d, extend as e, createPortal as f, reconciler as g, applyProps as h, dispose as i, invalidate as j, advance as k, addEffect as l, addAfterEffect as m, addTail as n, omit as o, pick as p, getRootState as q, render as r, act as s, threeTypes as t, useMemoizedFn as u, roots as v, useStore as w, useThree as x, useFrame as y, useGraph as z };