1
0

LocalOrbitControls.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. <script lang="ts" setup>
  2. import { useLoop, useTresContext } from '@tresjs/core'
  3. import { useEventListener } from '@vueuse/core'
  4. import { MOUSE, TOUCH } from 'three'
  5. import { OrbitControls } from 'three-stdlib'
  6. import { onUnmounted, shallowRef, toRefs, watch } from 'vue'
  7. import type { TresVector3 } from '@tresjs/core'
  8. import type { Camera } from 'three'
  9. export interface OrbitControlsProps {
  10. /**
  11. * Whether to make this the default controls.
  12. *
  13. * @default false
  14. * @type {boolean}
  15. * @memberof OrbitControlsProps
  16. */
  17. makeDefault?: boolean
  18. /**
  19. * The camera to control.
  20. *
  21. * @type {Camera}
  22. * @memberof OrbitControlsProps
  23. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.camera
  24. */
  25. camera?: Camera
  26. /**
  27. * The dom element to listen to.
  28. *
  29. * @type {HTMLElement}
  30. * @memberof OrbitControlsProps
  31. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.domElement
  32. */
  33. domElement?: HTMLElement
  34. /**
  35. * The target to orbit around.
  36. *
  37. * @type {TresVector3}
  38. * @memberof OrbitControlsProps
  39. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.target
  40. */
  41. target?: TresVector3
  42. /**
  43. * Whether to enable damping (inertia)
  44. *
  45. * @default false
  46. * @type {boolean}
  47. * @memberof OrbitControlsProps
  48. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.enableDamping
  49. */
  50. enableDamping?: boolean
  51. /**
  52. * The damping inertia used if `.enableDamping` is set to true
  53. *
  54. * @default 0.05
  55. * @type {number}
  56. * @memberof OrbitControlsProps
  57. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.dampingFactor
  58. */
  59. dampingFactor?: number
  60. /**
  61. * Set to true to automatically rotate around the target.
  62. *
  63. * @default false
  64. * @type {boolean}
  65. * @memberof OrbitControlsProps
  66. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.autoRotate
  67. */
  68. autoRotate?: boolean
  69. /**
  70. * How fast to rotate around the target if `.autoRotate` is true.
  71. *
  72. * @default 2
  73. * @type {number}
  74. * @memberof OrbitControlsProps
  75. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.autoRotateSpeed
  76. */
  77. autoRotateSpeed?: number
  78. /**
  79. * Whether to enable panning.
  80. *
  81. * @default true
  82. * @type {boolean}
  83. * @memberof OrbitControlsProps
  84. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.enablePan
  85. */
  86. enablePan?: boolean
  87. /**
  88. * How fast to pan the camera when the keyboard is used. Default is 7.0 pixels per keypress.
  89. *
  90. * @default 7.0
  91. * @type {number}
  92. * @memberof OrbitControlsProps
  93. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.keyPanSpeed
  94. */
  95. keyPanSpeed?: number
  96. /**
  97. * This object contains references to the keycodes for controlling camera panning.
  98. * Default is the 4 arrow keys.
  99. *
  100. * @default `{ LEFT: 'ArrowLeft', UP: 'ArrowUp', RIGHT: 'ArrowRight', BOTTOM: 'ArrowDown' }`
  101. * @type Record<string, string>
  102. * @memberof OrbitControlsProps
  103. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.keys
  104. */
  105. keys?: Record<string, string>
  106. /**
  107. * How far you can orbit horizontally, upper limit.
  108. * If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ],
  109. * with ( max - min < 2 PI ). Default is Infinity.
  110. *
  111. * @default Infinity
  112. * @type {number}
  113. * @memberof OrbitControlsProps
  114. * @see https://threejs.org/docs/index.html?q=orbi#examples/en/controls/OrbitControls.maxAzimuthAngle
  115. */
  116. maxAzimuthAngle?: number
  117. /**
  118. * How far you can orbit horizontally, lower limit.
  119. * If set, the interval [ min, max ] must be a sub-interval of [ - 2 PI, 2 PI ],
  120. * with ( max - min < 2 PI ).
  121. * Default is - Infinity.
  122. *
  123. * @default -Infinity
  124. * @type {number}
  125. * @memberof OrbitControlsProps
  126. * @see https://threejs.org/docs/index.html?q=orbi#examples/en/controls/OrbitControls.minAzimuthAngle
  127. */
  128. minAzimuthAngle?: number
  129. /**
  130. * How far you can orbit vertically, upper limit.
  131. * Range is 0 to Math.PI radians, and default is Math.PI.
  132. *
  133. * @default Math.PI
  134. * @type {number}
  135. * @memberof OrbitControlsProps
  136. * @see https://threejs.org/docs/index.html?q=orbi#examples/en/controls/OrbitControls.maxPolarAngle
  137. */
  138. maxPolarAngle?: number
  139. /**
  140. * How far you can orbit vertically, lower limit.
  141. * Range is 0 to Math.PI radians, and default is 0.
  142. *
  143. * @default 0
  144. * @type {number}
  145. * @memberof OrbitControlsProps
  146. * @see https://threejs.org/docs/index.html?q=orbi#examples/en/controls/OrbitControls.minPolarAngle
  147. */
  148. minPolarAngle?: number
  149. /**
  150. * The minimum distance of the camera to the target.
  151. * Default is 0.
  152. *
  153. * @default 0
  154. * @type {number}
  155. * @memberof OrbitControlsProps
  156. * @see https://threejs.org/docs/index.html?q=orbi#examples/en/controls/OrbitControls.minDistance
  157. */
  158. minDistance?: number
  159. /**
  160. * The maximum distance of the camera to the target.
  161. * Default is Infinity.
  162. *
  163. * @default Infinity
  164. * @type {number}
  165. * @memberof OrbitControlsProps
  166. * @see https://threejs.org/docs/index.html?q=orbi#examples/en/controls/OrbitControls.maxDistance
  167. */
  168. maxDistance?: number
  169. /**
  170. * The minimum field of view angle, in radians.
  171. * Default is 0.
  172. *
  173. * @default 0
  174. * @type {number}
  175. * @memberof OrbitControlsProps
  176. * @see https://threejs.org/docs/index.html?q=orbi#examples/en/controls/OrbitControls.minZoom
  177. */
  178. minZoom?: number
  179. /**
  180. * The maximum field of view angle, in radians.
  181. * ( OrthographicCamera only ).
  182. * Default is Infinity.
  183. *
  184. * @default Infinity
  185. * @type {number}
  186. * @memberof OrbitControlsProps
  187. * @see https://threejs.org/docs/index.html?q=orbi#examples/en/controls/OrbitControls.maxZoom
  188. */
  189. maxZoom?: number
  190. touches?: {
  191. ONE?: number | undefined
  192. TWO?: number | undefined
  193. }
  194. /**
  195. * Whether to enable zooming.
  196. *
  197. * @default true
  198. * @type {boolean}
  199. * @memberof OrbitControlsProps
  200. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.enableZoom
  201. */
  202. enableZoom?: boolean
  203. /**
  204. * How fast to zoom in and out. Default is 1.
  205. *
  206. * @default 1
  207. * @type {number}
  208. * @memberof OrbitControlsProps
  209. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.zoomSpeed
  210. */
  211. zoomSpeed?: number
  212. /**
  213. * Whether to enable rotating.
  214. *
  215. * @default true
  216. * @type {boolean}
  217. * @memberof OrbitControlsProps
  218. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.enableRotate
  219. */
  220. enableRotate?: boolean
  221. /**
  222. * How fast to rotate around the target. Default is 1.
  223. *
  224. * @default 1
  225. * @type {number}
  226. * @memberof OrbitControlsProps
  227. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.rotateSpeed
  228. */
  229. rotateSpeed?: number
  230. /**
  231. * This object contains references to the mouse actions used by the controls.
  232. * LEFT: Rotate around the target
  233. * MIDDLE: Zoom the camera
  234. * RIGHT: Pan the camera
  235. *
  236. * @default { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN }
  237. * @type {{ LEFT?: number, MIDDLE?: number, RIGHT?: number }}
  238. * @memberof OrbitControlsProps
  239. * @see https://threejs.org/docs/#examples/en/controls/OrbitControls.mouseButtons
  240. */
  241. mouseButtons?: {
  242. LEFT?: number
  243. MIDDLE?: number
  244. RIGHT?: number
  245. }
  246. }
  247. const props = withDefaults(defineProps<OrbitControlsProps>(), {
  248. makeDefault: false,
  249. autoRotate: false,
  250. autoRotateSpeed: 2,
  251. enableDamping: true,
  252. dampingFactor: 0.05,
  253. enablePan: true,
  254. keyPanSpeed: 7,
  255. maxAzimuthAngle: Number.POSITIVE_INFINITY,
  256. minAzimuthAngle: Number.NEGATIVE_INFINITY,
  257. maxPolarAngle: Math.PI,
  258. minPolarAngle: 0,
  259. minDistance: 0,
  260. maxDistance: Number.POSITIVE_INFINITY,
  261. minZoom: 0,
  262. maxZoom: Number.POSITIVE_INFINITY,
  263. enableZoom: true,
  264. zoomSpeed: 1,
  265. enableRotate: true,
  266. touches: () => ({ ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN }),
  267. rotateSpeed: 1,
  268. target: () => [0, 0, 0],
  269. mouseButtons: () => ({ LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN }),
  270. })
  271. const emit = defineEmits(['change', 'start', 'end'])
  272. const {
  273. makeDefault,
  274. autoRotate,
  275. autoRotateSpeed,
  276. enableDamping,
  277. dampingFactor,
  278. enablePan,
  279. keyPanSpeed,
  280. maxAzimuthAngle,
  281. minAzimuthAngle,
  282. maxPolarAngle,
  283. minPolarAngle,
  284. minDistance,
  285. maxDistance,
  286. minZoom,
  287. maxZoom,
  288. enableZoom,
  289. zoomSpeed,
  290. enableRotate,
  291. touches,
  292. rotateSpeed,
  293. target,
  294. mouseButtons,
  295. } = toRefs(props)
  296. const { camera: activeCamera, renderer, extend, controls, invalidate } = useTresContext()
  297. const controlsRef = shallowRef<OrbitControls | null>(null)
  298. extend({ OrbitControls })
  299. watch(controlsRef, (value) => {
  300. addEventListeners()
  301. if (value && makeDefault.value) {
  302. controls.value = value
  303. }
  304. else {
  305. controls.value = null
  306. }
  307. })
  308. function addEventListeners() {
  309. useEventListener(controlsRef.value as any, 'change', () => {
  310. emit('change', controlsRef.value)
  311. invalidate()
  312. })
  313. useEventListener(controlsRef.value as any, 'start', () => emit('start', controlsRef.value))
  314. useEventListener(controlsRef.value as any, 'end', () => emit('end', controlsRef.value))
  315. }
  316. const { onBeforeRender } = useLoop()
  317. onBeforeRender(({ invalidate }) => {
  318. if (controlsRef.value && (enableDamping.value || autoRotate.value)) {
  319. controlsRef.value.update()
  320. if (autoRotate.value) {
  321. invalidate()
  322. }
  323. }
  324. })
  325. onUnmounted(() => {
  326. if (controlsRef.value) {
  327. controlsRef.value.dispose()
  328. }
  329. })
  330. defineExpose({ instance: controlsRef })
  331. </script>
  332. <template>
  333. <TresOrbitControls
  334. v-if="(camera || activeCamera) && (domElement || renderer)"
  335. ref="controlsRef"
  336. :target="target"
  337. :auto-rotate="autoRotate"
  338. :auto-rotate-speed="autoRotateSpeed"
  339. :enable-damping="enableDamping"
  340. :damping-factor="dampingFactor"
  341. :enable-pan="enablePan"
  342. :key-pan-speed="keyPanSpeed"
  343. :keys="keys"
  344. :max-azimuth-angle="maxAzimuthAngle"
  345. :min-azimuth-angle="minAzimuthAngle"
  346. :max-polar-angle="maxPolarAngle"
  347. :min-polar-angle="minPolarAngle"
  348. :min-distance="minDistance"
  349. :max-distance="maxDistance"
  350. :min-zoom="minZoom"
  351. :max-zoom="maxZoom"
  352. :touches="touches"
  353. :enable-zoom="enableZoom"
  354. :zoom-speed="zoomSpeed"
  355. :enable-rotate="enableRotate"
  356. :rotate-speed="rotateSpeed"
  357. :mouse-buttons="mouseButtons"
  358. :args="[camera || activeCamera, domElement || renderer.domElement]"
  359. />
  360. </template>