-
Notifications
You must be signed in to change notification settings - Fork 730
/
Copy pathArcballControls.tsx
77 lines (67 loc) · 2.86 KB
/
ArcballControls.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import { EventManager, ReactThreeFiber, useFrame, useThree } from '@react-three/fiber'
import * as React from 'react'
import { forwardRef, useEffect, useMemo } from 'react'
import { ArcballControls as ArcballControlsImpl } from 'three-stdlib'
import type { Event, OrthographicCamera, PerspectiveCamera } from 'three'
import { ForwardRefComponent } from '../helpers/ts-utils'
export type ArcballControlsProps = Omit<
ReactThreeFiber.Overwrite<
ReactThreeFiber.Object3DNode<ArcballControlsImpl, typeof ArcballControlsImpl>,
{
target?: ReactThreeFiber.Vector3
camera?: OrthographicCamera | PerspectiveCamera
domElement?: HTMLElement
regress?: boolean
makeDefault?: boolean
onChange?: (e?: Event) => void
onStart?: (e?: Event) => void
onEnd?: (e?: Event) => void
}
>,
'ref'
>
export const ArcballControls: ForwardRefComponent<ArcballControlsProps, ArcballControlsImpl> =
/* @__PURE__ */ forwardRef<ArcballControlsImpl, ArcballControlsProps>(
({ camera, makeDefault, regress, domElement, onChange, onStart, onEnd, ...restProps }, ref) => {
const invalidate = useThree((state) => state.invalidate)
const defaultCamera = useThree((state) => state.camera)
const gl = useThree((state) => state.gl)
const events = useThree((state) => state.events) as EventManager<HTMLElement>
const set = useThree((state) => state.set)
const get = useThree((state) => state.get)
const performance = useThree((state) => state.performance)
const explCamera = camera || defaultCamera
const explDomElement = (domElement || events.connected || gl.domElement) as HTMLElement
const controls = useMemo(() => new ArcballControlsImpl(explCamera), [explCamera])
useFrame(() => {
if (controls.enabled) controls.update()
}, -1)
useEffect(() => {
controls.connect(explDomElement)
return () => void controls.dispose()
}, [explDomElement, regress, controls, invalidate])
useEffect(() => {
const callback = (e: Event) => {
invalidate()
if (regress) performance.regress()
if (onChange) onChange(e)
}
controls.addEventListener('change', callback)
if (onStart) controls.addEventListener('start', onStart)
if (onEnd) controls.addEventListener('end', onEnd)
return () => {
controls.removeEventListener('change', callback)
if (onStart) controls.removeEventListener('start', onStart)
if (onEnd) controls.removeEventListener('end', onEnd)
}
}, [onChange, onStart, onEnd])
useEffect(() => {
if (makeDefault) {
const old = get().controls
set({ controls })
return () => set({ controls: old })
}
}, [makeDefault, controls])
return <primitive ref={ref} object={controls} {...restProps} />
}
)