@expofp/renderer 3.36.0 → 3.37.1

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.
@@ -60,7 +60,7 @@ export function CameraControls(props) {
60
60
  setControls((controls) => Object.assign(controls, { camera: explCamera }));
61
61
  }
62
62
  useFrame((_state, delta) => {
63
- const updated = controls.update(delta);
63
+ const updated = controls.update(Math.min(delta, MAX_FRAME_DELTA));
64
64
  if (updated || gl.info.render.frame == 0) {
65
65
  const position = explCamera.position.toArray().map((value) => +value.toFixed(2));
66
66
  const target = controls
@@ -190,5 +190,14 @@ function createControls(camera) {
190
190
  * tolerates next to a component, and this one never changes.
191
191
  */
192
192
  export const CameraControlsAction = CameraControlsImpl.ACTION;
193
+ /**
194
+ * The longest step, in seconds, one frame may advance the controls' easing by.
195
+ *
196
+ * Under a demand frameloop the first frame after an idle spell carries the whole spell as its
197
+ * delta, and camera-controls does not clamp it: a move started by that frame would reach its
198
+ * target in one step. A ceiling rather than a fixed step, so a device drawing at 30 fps still
199
+ * eases in real time and only the idle gap is cut short.
200
+ */
201
+ const MAX_FRAME_DELTA = 1 / 30;
193
202
  const _target = new Vector3();
194
203
  const _spherical = new Spherical();
@@ -37,17 +37,43 @@ export declare function useCameraControls(): CameraControlsImpl | undefined;
37
37
  */
38
38
  export declare function useControlsEffect(effect: (controls: CameraControlsImpl) => (() => void) | void): void;
39
39
  /**
40
- * Runs a handler when a camera movement begins — a gesture and a command alike, one call per
41
- * movement rather than one per frame. The event the imperative renderer published as
42
- * `navigation:change`, under a name that says what happened.
43
- *
44
- * A camera-level event rather than the controls themselves, so a caller resetting an idle timer or
45
- * dismissing an overlay never learns which library drives the camera. For as long as the component
46
- * is mounted; where the camera *is* once it settles is `useCameraState`'s business.
47
- * @param handler - Called as each movement starts. Re-subscribed when its identity changes, so pass
48
- * a stable function — under the React Compiler an inline closure over stable values is one.
40
+ * Runs a handler once the camera has come to rest after moving — one call per movement rather than
41
+ * one per frame, whether a gesture or a command drove it. The event the imperative renderer
42
+ * published as `navigation:change`, under a name that says what happened.
43
+ *
44
+ * At rest and not at the start, so a caller that reads the camera back reads where the movement
45
+ * actually ended: the framing a kiosk saves, or the rect an overlay measures itself against. Every
46
+ * movement is reported because the one that moved the camera is not always the one that cares — the
47
+ * zoom buttons reach the camera through the store, and the screen recording the framing never sees
48
+ * that command.
49
+ *
50
+ * `sleep` rather than `rest`, which the library recommends for "the camera has stopped": `rest` is
51
+ * dispatched from the damping tail, and a pan here has none to dispatch from ({@link Controls}
52
+ * zeroes the control smoothing for truck and rotate), so a drag can finish without ever resting.
53
+ * `sleep` is dispatched for every movement, a jump included.
54
+ *
55
+ * A camera-level event rather than the controls themselves, so a caller recording where the plan was
56
+ * left never learns which library drives the camera. For as long as the component is mounted; where
57
+ * the camera *is* once it settles is `useCameraState`'s business.
58
+ * @param handler - Called once each movement has come to rest. Re-subscribed when its identity
59
+ * changes, so pass a stable function — under the React Compiler an inline closure over stable
60
+ * values is one.
61
+ */
62
+ export declare function useCameraMoveEnd(handler: () => void): void;
63
+ /**
64
+ * Runs a handler while the user is moving the camera by hand — a drag, a pinch and a wheel tick
65
+ * alike, as each one reaches the camera.
66
+ *
67
+ * The user's own movement and nothing else, which is what an idle timer wants: a kiosk that treated
68
+ * a command as activity would have the idle reset's own camera restore re-arm the timer it had just
69
+ * fired, and would never return home. Deliberately not bracketed by the gesture's start and end —
70
+ * the controls report neither for the wheel, since scrolling arrives in intermittent ticks with no
71
+ * way to tell a last one from a pause.
72
+ * @param handler - Called as each movement of the user's reaches the camera, which may be once per
73
+ * frame for as long as a drag lasts. Re-subscribed when its identity changes, so pass a stable
74
+ * function — under the React Compiler an inline closure over stable values is one.
49
75
  */
50
- export declare function useCameraMoveStart(handler: () => void): void;
76
+ export declare function useCameraGesture(handler: () => void): void;
51
77
  /** Where the camera is, at the moment it was asked. */
52
78
  export interface CameraState {
53
79
  /** The point on the plan it is looking at, in plan units. */
@@ -56,20 +56,51 @@ export function useControlsEffect(effect) {
56
56
  }, [effect, get, mounted]);
57
57
  }
58
58
  /**
59
- * Runs a handler when a camera movement begins — a gesture and a command alike, one call per
60
- * movement rather than one per frame. The event the imperative renderer published as
61
- * `navigation:change`, under a name that says what happened.
62
- *
63
- * A camera-level event rather than the controls themselves, so a caller resetting an idle timer or
64
- * dismissing an overlay never learns which library drives the camera. For as long as the component
65
- * is mounted; where the camera *is* once it settles is `useCameraState`'s business.
66
- * @param handler - Called as each movement starts. Re-subscribed when its identity changes, so pass
67
- * a stable function — under the React Compiler an inline closure over stable values is one.
59
+ * Runs a handler once the camera has come to rest after moving — one call per movement rather than
60
+ * one per frame, whether a gesture or a command drove it. The event the imperative renderer
61
+ * published as `navigation:change`, under a name that says what happened.
62
+ *
63
+ * At rest and not at the start, so a caller that reads the camera back reads where the movement
64
+ * actually ended: the framing a kiosk saves, or the rect an overlay measures itself against. Every
65
+ * movement is reported because the one that moved the camera is not always the one that cares — the
66
+ * zoom buttons reach the camera through the store, and the screen recording the framing never sees
67
+ * that command.
68
+ *
69
+ * `sleep` rather than `rest`, which the library recommends for "the camera has stopped": `rest` is
70
+ * dispatched from the damping tail, and a pan here has none to dispatch from ({@link Controls}
71
+ * zeroes the control smoothing for truck and rotate), so a drag can finish without ever resting.
72
+ * `sleep` is dispatched for every movement, a jump included.
73
+ *
74
+ * A camera-level event rather than the controls themselves, so a caller recording where the plan was
75
+ * left never learns which library drives the camera. For as long as the component is mounted; where
76
+ * the camera *is* once it settles is `useCameraState`'s business.
77
+ * @param handler - Called once each movement has come to rest. Re-subscribed when its identity
78
+ * changes, so pass a stable function — under the React Compiler an inline closure over stable
79
+ * values is one.
80
+ */
81
+ export function useCameraMoveEnd(handler) {
82
+ useControlsEffect((controls) => {
83
+ controls.addEventListener('sleep', handler);
84
+ return () => controls.removeEventListener('sleep', handler);
85
+ });
86
+ }
87
+ /**
88
+ * Runs a handler while the user is moving the camera by hand — a drag, a pinch and a wheel tick
89
+ * alike, as each one reaches the camera.
90
+ *
91
+ * The user's own movement and nothing else, which is what an idle timer wants: a kiosk that treated
92
+ * a command as activity would have the idle reset's own camera restore re-arm the timer it had just
93
+ * fired, and would never return home. Deliberately not bracketed by the gesture's start and end —
94
+ * the controls report neither for the wheel, since scrolling arrives in intermittent ticks with no
95
+ * way to tell a last one from a pause.
96
+ * @param handler - Called as each movement of the user's reaches the camera, which may be once per
97
+ * frame for as long as a drag lasts. Re-subscribed when its identity changes, so pass a stable
98
+ * function — under the React Compiler an inline closure over stable values is one.
68
99
  */
69
- export function useCameraMoveStart(handler) {
100
+ export function useCameraGesture(handler) {
70
101
  useControlsEffect((controls) => {
71
- controls.addEventListener('transitionstart', handler);
72
- return () => controls.removeEventListener('transitionstart', handler);
102
+ controls.addEventListener('control', handler);
103
+ return () => controls.removeEventListener('control', handler);
73
104
  });
74
105
  }
75
106
  /**
@@ -4,5 +4,5 @@ export { Controls, type ControlsConfig, type ControlsProps } from './Controls';
4
4
  export type { InertiaConfig } from './handlers/Inertia';
5
5
  export type { PanConfig } from './handlers/Pan';
6
6
  export type { RollConfig } from './handlers/Roll';
7
- export { type CameraState, useCameraAPI, useCameraControls, useCameraMoveStart, useCameraState, useControlsEffect, } from './hooks';
7
+ export { type CameraState, useCameraAPI, useCameraControls, useCameraGesture, useCameraMoveEnd, useCameraState, useControlsEffect, } from './hooks';
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -4,4 +4,4 @@
4
4
  export { bearing, CameraCommands, } from './camera-api';
5
5
  export { CameraControls, CameraControlsAction } from './CameraControls';
6
6
  export { Controls } from './Controls';
7
- export { useCameraAPI, useCameraControls, useCameraMoveStart, useCameraState, useControlsEffect, } from './hooks';
7
+ export { useCameraAPI, useCameraControls, useCameraGesture, useCameraMoveEnd, useCameraState, useControlsEffect, } from './hooks';
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type CameraAPI, type CameraAxes, type CameraState, type ControlsConfig, type InertiaConfig, type PanConfig, type RollConfig, type TransitionOptions, useCameraAPI, useCameraMoveStart, useCameraState, type ZoomToOptions, } from './controls';
1
+ export { type CameraAPI, type CameraAxes, type CameraState, type ControlsConfig, type InertiaConfig, type PanConfig, type RollConfig, type TransitionOptions, useCameraAPI, useCameraGesture, useCameraMoveEnd, useCameraState, type ZoomToOptions, } from './controls';
2
2
  export { prepareDebug } from './debug/debug-chunk';
3
3
  export { useDebugPanel } from './debug/debug-context';
4
4
  export { type LightingProps, type ShadowProps } from './objects/lighting';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- // The camera's public surface: commands, a state reader, and the movement event. Deliberately not
2
- // `useControlsEffect` or the controls themselves — those hand out `camera-controls`, which is this
3
- // renderer's internals.
4
- export { useCameraAPI, useCameraMoveStart, useCameraState, } from './controls';
1
+ // The camera's public surface: commands, a state reader, and the two movement events — the user's
2
+ // own hand on the camera, and any movement coming to rest. Deliberately not `useControlsEffect` or
3
+ // the controls themselves — those hand out `camera-controls`, which is this renderer's internals.
4
+ export { useCameraAPI, useCameraGesture, useCameraMoveEnd, useCameraState, } from './controls';
5
5
  // The debug chunk's preload, for an external host that must create its context instrumented.
6
6
  export { prepareDebug } from './debug/debug-chunk';
7
7
  // Publishing a host's own values into the debug panel.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expofp/renderer",
3
- "version": "3.36.0",
3
+ "version": "3.37.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/expofp/efp-app.git",
@@ -42,7 +42,7 @@
42
42
  "troika-three-text": "^0.52.5",
43
43
  "tslib": "^2.3.0",
44
44
  "webgl-memory": "^1.1.3",
45
- "@expofp/geometry": "3.36.0"
45
+ "@expofp/geometry": "3.37.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@react-three/test-renderer": "^9.1.0",