threepipe
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

camera-anim.ts 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import {Quaternion, Spherical, Vector3} from 'three'
  2. import {worldToLocalQuaternion} from '../three/utils'
  3. import {CameraView, ICamera, ICameraView} from '../core'
  4. import {AnimationOptions} from 'popmotion'
  5. import {lerp, lerpAngle} from './animation'
  6. export function sphericalFromCameraView(view: Pick<CameraView, 'position'|'target'>): Spherical {
  7. const pos = view.position.clone()
  8. pos.sub(view.target)
  9. const spherical = new Spherical().setFromVector3(pos)
  10. spherical.makeSafe() // todo: is it needed?
  11. return spherical
  12. }
  13. export function animateCameraToViewSpherical(camera: ICamera, view: ICameraView): AnimationOptions<number> {
  14. // similar to orbit controls
  15. const parent = camera.parent
  16. const target = camera.target.clone()
  17. const position = camera.getWorldPosition(new Vector3())
  18. const init = {
  19. position, target, zoom: camera.zoom,
  20. spherical: sphericalFromCameraView({position, target}),
  21. }
  22. const current = {
  23. position: new Vector3(),
  24. target: new Vector3(),
  25. zoom: 1,
  26. spherical: new Spherical(),
  27. }
  28. const final = {
  29. position: view.position,
  30. target: view.target,
  31. zoom: view.zoom,
  32. spherical: sphericalFromCameraView(view),
  33. }
  34. function setter() {
  35. camera.position.copy(parent ? parent.worldToLocal(current.position) : current.position)
  36. camera.target.copy(current.target) // always in world space
  37. camera.zoom = current.zoom
  38. // lookAt in setDirty updates the quaternion
  39. camera.setDirty() // because it has min change distance in setter
  40. }
  41. return {
  42. from: 0,
  43. to: 1,
  44. onUpdate: (v) => {
  45. current.spherical.phi = lerpAngle(init.spherical.phi, final.spherical.phi, v)
  46. current.spherical.theta = lerpAngle(init.spherical.theta, final.spherical.theta, v)
  47. current.spherical.radius = lerp(init.spherical.radius, final.spherical.radius, v)
  48. current.target.copy(init.target).lerp(final.target, v)
  49. current.position.setFromSpherical(current.spherical)
  50. current.position.add(current.target)
  51. current.zoom = lerp(init.zoom, final.zoom, v)
  52. setter()
  53. },
  54. onComplete: () => {
  55. current.position.copy(final.position)
  56. current.target.copy(final.target)
  57. current.zoom = final.zoom
  58. setter()
  59. },
  60. onStop: () => {
  61. throw new Error('Animation Stopped')
  62. },
  63. }
  64. }
  65. export function animateCameraToViewLinear(camera: ICamera, view: ICameraView): AnimationOptions<number> {
  66. // similar to orbit controls
  67. // so camera.up is the orbit axis
  68. const parent = camera.parent
  69. const target = camera.target.clone()
  70. const position = camera.getWorldPosition(new Vector3())
  71. const quaternion = camera.getWorldQuaternion(new Quaternion())
  72. const init = {
  73. position, target, quaternion, zoom: camera.zoom,
  74. }
  75. const current = {
  76. position: new Vector3(),
  77. target: new Vector3(),
  78. quaternion: new Quaternion(),
  79. zoom: 1,
  80. }
  81. const final = view
  82. function setter() {
  83. camera.position.copy(parent ? parent.worldToLocal(current.position) : current.position)
  84. camera.target.copy(current.target) // always in world space
  85. camera.quaternion.copy(parent ? worldToLocalQuaternion(parent, current.quaternion, camera.quaternion) : current.quaternion)
  86. camera.zoom = current.zoom
  87. camera.setDirty() // because it has min change distance in setter
  88. }
  89. return {
  90. from: 0,
  91. to: 1,
  92. onUpdate: (v) => {
  93. current.position.lerpVectors(init.position, final.position, v)
  94. current.target.lerpVectors(init.target, final.target, v)
  95. current.quaternion.slerpQuaternions(init.quaternion, final.quaternion, v)
  96. current.zoom = lerp(init.zoom, final.zoom, v)
  97. setter()
  98. },
  99. onComplete: () => {
  100. current.position.copy(final.position)
  101. current.target.copy(final.target)
  102. current.quaternion.copy(final.quaternion)
  103. current.zoom = final.zoom
  104. setter()
  105. },
  106. onStop: () => {
  107. throw new Error('Animation Stopped')
  108. },
  109. }
  110. }