diff --git a/.storybook/stories/CameraControls.stories.tsx b/.storybook/stories/CameraControls.stories.tsx index 40ec47c48..d8749951f 100644 --- a/.storybook/stories/CameraControls.stories.tsx +++ b/.storybook/stories/CameraControls.stories.tsx @@ -9,22 +9,17 @@ import { Box, CameraControls, PerspectiveCamera, Plane, useFBO } from '../../src export default { title: 'Controls/CameraControls', component: CameraControls, - decorators: [ - (Story) => ( - - - - ), - ], } satisfies Meta type Story = StoryObj +// + function CameraControlsScene1(props: ComponentProps) { const cameraControlRef = useRef(null) return ( - <> + { @@ -33,7 +28,7 @@ function CameraControlsScene1(props: ComponentProps) { > - + ) } @@ -42,6 +37,8 @@ export const CameraControlsSt1 = { name: 'Default', } satisfies Story +// + const CameraControlsScene2 = (props: ComponentProps) => { /** * we will render our scene in a render target and use it as a map. @@ -90,6 +87,47 @@ const CameraControlsScene2 = (props: ComponentProps) => { } export const CameraControlsSt2 = { - render: (args) => , + render: (args) => ( + + + + ), name: 'Custom Camera', } satisfies Story + +// + +function CameraControlsScene3(props: ComponentProps) { + const cameraControlRef = useRef(null) + + return ( + <> + console.log('wake')} + // onSleep={() => console.log('sleep')} + /> + { + cameraControlRef.current?.rotate(Math.PI / 4, 0, true) + }} + > + + + + ) +} + +export const CameraControlsSt3 = { + render: (args) => ( + + + + ), + name: 'frameloop="demand"', +} satisfies Story diff --git a/docs/controls/camera-controls.mdx b/docs/controls/camera-controls.mdx index 306eecb7e..9d2601fd5 100644 --- a/docs/controls/camera-controls.mdx +++ b/docs/controls/camera-controls.mdx @@ -24,9 +24,14 @@ type CameraControlsProps = { /** Reference this CameraControls instance as state's `controls` */ makeDefault?: boolean /** Events callbacks, see: https://github.com/yomotsu/camera-controls#events */ - onStart?: (e?: { type: 'controlstart' }) => void - onEnd?: (e?: { type: 'controlend' }) => void - onChange?: (e?: { type: 'update' }) => void + onControlStart?: (e? { type: 'controlstart' }) => void + onControl?: (e? { type: 'control' }) => void + onControlEnd?: (e? { type: 'controlend' }) => void + onTransitionStart?: (e? { type: 'transitionstart' }) => void + onUpdate?: (e? { type: 'update' }) => void + onWake?: (e? { type: 'wake' }) => void + onRest?: (e? { type: 'rest' }) => void + onSleep?: (e? { type: 'sleep' }) => void } ``` diff --git a/src/core/CameraControls.tsx b/src/core/CameraControls.tsx index 6e86719f3..d38f65eab 100644 --- a/src/core/CameraControls.tsx +++ b/src/core/CameraControls.tsx @@ -1,3 +1,4 @@ +/* eslint react-hooks/exhaustive-deps: 1 */ import { Box3, EventDispatcher, @@ -28,9 +29,23 @@ export type CameraControlsProps = Omit< camera?: PerspectiveCamera | OrthographicCamera domElement?: HTMLElement makeDefault?: boolean + + onControlStart?: (e?: { type: 'controlstart' }) => void + onControl?: (e?: { type: 'control' }) => void + onControlEnd?: (e?: { type: 'controlend' }) => void + onTransitionStart?: (e?: { type: 'transitionstart' }) => void + onUpdate?: (e?: { type: 'update' }) => void + onWake?: (e?: { type: 'wake' }) => void + onRest?: (e?: { type: 'rest' }) => void + onSleep?: (e?: { type: 'sleep' }) => void + + /** @deprecated for OrbitControls compatibility: use `onControlStart` instead */ onStart?: (e?: { type: 'controlstart' }) => void + /** @deprecated for OrbitControls compatibility: use `onControlEnd` instead */ onEnd?: (e?: { type: 'controlend' }) => void - onChange?: (e?: { type: 'update' }) => void + /** @deprecated for OrbitControls compatibility */ + onChange?: (e?: { type: string }) => void + events?: boolean // Wether to enable events during controls interaction regress?: boolean } @@ -65,7 +80,24 @@ export const CameraControls: ForwardRefComponent state.camera) const gl = useThree((state) => state.gl) @@ -91,36 +123,95 @@ export const CameraControls: ForwardRefComponent { - const callback = (e) => { + function invalidateAndRegress() { invalidate() if (regress) performance.regress() - if (onChange) onChange(e) } - const onStartCb: CameraControlsProps['onStart'] = (e) => { - if (onStart) onStart(e) + const handleControlStart = (e: { type: 'controlstart' }) => { + invalidateAndRegress() + onControlStart?.(e) + onStart?.(e) // backwards compatibility + } + + const handleControl = (e: { type: 'control' }) => { + invalidateAndRegress() + onControl?.(e) + onChange?.(e) // backwards compatibility } - const onEndCb: CameraControlsProps['onEnd'] = (e) => { - if (onEnd) onEnd(e) + const handleControlEnd = (e: { type: 'controlend' }) => { + onControlEnd?.(e) + onEnd?.(e) // backwards compatibility } - controls.addEventListener('update', callback) - controls.addEventListener('controlstart', onStartCb) - controls.addEventListener('controlend', onEndCb) - controls.addEventListener('control', callback) - controls.addEventListener('transitionstart', callback) - controls.addEventListener('wake', callback) + const handleTransitionStart = (e: { type: 'transitionstart' }) => { + invalidateAndRegress() + onTransitionStart?.(e) + onChange?.(e) // backwards compatibility + } + + const handleUpdate = (e: { type: 'update' }) => { + invalidateAndRegress() + onUpdate?.(e) + onChange?.(e) // backwards compatibility + } + + const handleWake = (e: { type: 'wake' }) => { + invalidateAndRegress() + onWake?.(e) + onChange?.(e) // backwards compatibility + } + + const handleRest = (e: { type: 'rest' }) => { + onRest?.(e) + } + + const handleSleep = (e: { type: 'sleep' }) => { + onSleep?.(e) + } + + controls.addEventListener('controlstart', handleControlStart) + controls.addEventListener('control', handleControl) + controls.addEventListener('controlend', handleControlEnd) + controls.addEventListener('transitionstart', handleTransitionStart) + controls.addEventListener('update', handleUpdate) + controls.addEventListener('wake', handleWake) + controls.addEventListener('rest', handleRest) + controls.addEventListener('sleep', handleSleep) return () => { - controls.removeEventListener('update', callback) - controls.removeEventListener('controlstart', onStartCb) - controls.removeEventListener('controlend', onEndCb) - controls.removeEventListener('control', callback) - controls.removeEventListener('transitionstart', callback) - controls.removeEventListener('wake', callback) + controls.removeEventListener('controlstart', handleControlStart) + controls.removeEventListener('control', handleControl) + controls.removeEventListener('controlend', handleControlEnd) + controls.removeEventListener('transitionstart', handleTransitionStart) + controls.removeEventListener('update', handleUpdate) + controls.removeEventListener('wake', handleWake) + controls.removeEventListener('rest', handleRest) + controls.removeEventListener('sleep', handleSleep) } - }, [controls, onStart, onEnd, invalidate, setEvents, regress, onChange]) + }, [ + controls, + + invalidate, + setEvents, + regress, + + performance, + + onControlStart, + onControl, + onControlEnd, + onTransitionStart, + onUpdate, + onWake, + onRest, + onSleep, + + onChange, + onStart, + onEnd, + ]) useEffect(() => { if (makeDefault) { @@ -128,6 +219,7 @@ export const CameraControls: ForwardRefComponent set({ controls: old }) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [makeDefault, controls]) return