index.ts 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import type { EventHookOn, Fn } from '@vueuse/core'
  2. import type { Ref } from 'vue'
  3. import { createEventHook, useRafFn } from '@vueuse/core'
  4. import { Clock } from 'three'
  5. export interface RenderLoop {
  6. delta: number
  7. elapsed: number
  8. clock: Clock
  9. }
  10. export interface UseRenderLoopReturn {
  11. onBeforeLoop: EventHookOn<RenderLoop>
  12. onLoop: EventHookOn<RenderLoop>
  13. onAfterLoop: EventHookOn<RenderLoop>
  14. pause: Fn
  15. resume: Fn
  16. isActive: Ref<boolean>
  17. }
  18. const onBeforeLoop = createEventHook<RenderLoop>()
  19. const onLoop = createEventHook<RenderLoop>()
  20. const onAfterLoop = createEventHook<RenderLoop>()
  21. const clock = new Clock()
  22. let delta = 0
  23. let elapsed = 0
  24. const { pause, resume, isActive } = useRafFn(
  25. () => {
  26. onBeforeLoop.trigger({ delta, elapsed, clock })
  27. onLoop.trigger({ delta, elapsed, clock })
  28. onAfterLoop.trigger({ delta, elapsed, clock })
  29. },
  30. { immediate: false },
  31. )
  32. onAfterLoop.on(() => {
  33. delta = clock.getDelta()
  34. elapsed = clock.getElapsedTime()
  35. })
  36. let startedOnce = false
  37. export const useRenderLoop = (): UseRenderLoopReturn => {
  38. if (!startedOnce) {
  39. // NOTE: `useRenderLoop` is not started by default
  40. // in order not to waste user resources. Instead, we'll
  41. // start the loop the first time the user uses
  42. // `useRenderLoop`.
  43. startedOnce = true
  44. resume()
  45. }
  46. return {
  47. onBeforeLoop: onBeforeLoop.on,
  48. onLoop: onLoop.on,
  49. onAfterLoop: onAfterLoop.on,
  50. pause,
  51. resume,
  52. isActive,
  53. }
  54. }