autoRotationBehavior.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. module BABYLON {
  2. /**
  3. * The autoRotation behavior (BABYLON.AutoRotationBehavior) is designed to create a smooth rotation of an ArcRotateCamera when there is no user interaction.
  4. * @see http://doc.babylonjs.com/how_to/camera_behaviors#autorotation-behavior
  5. */
  6. export class AutoRotationBehavior implements Behavior<ArcRotateCamera> {
  7. /**
  8. * Gets the name of the behavior.
  9. */
  10. public get name(): string {
  11. return "AutoRotation";
  12. }
  13. private _zoomStopsAnimation = false;
  14. private _idleRotationSpeed = 0.05;
  15. private _idleRotationWaitTime = 2000;
  16. private _idleRotationSpinupTime = 2000;
  17. /**
  18. * Sets the flag that indicates if user zooming should stop animation.
  19. */
  20. public set zoomStopsAnimation(flag: boolean) {
  21. this._zoomStopsAnimation = flag;
  22. }
  23. /**
  24. * Gets the flag that indicates if user zooming should stop animation.
  25. */
  26. public get zoomStopsAnimation(): boolean {
  27. return this._zoomStopsAnimation;
  28. }
  29. /**
  30. * Sets the default speed at which the camera rotates around the model.
  31. */
  32. public set idleRotationSpeed(speed: number) {
  33. this._idleRotationSpeed = speed;
  34. }
  35. /**
  36. * Gets the default speed at which the camera rotates around the model.
  37. */
  38. public get idleRotationSpeed() {
  39. return this._idleRotationSpeed;
  40. }
  41. /**
  42. * Sets the time (in milliseconds) to wait after user interaction before the camera starts rotating.
  43. */
  44. public set idleRotationWaitTime(time: number) {
  45. this._idleRotationWaitTime = time;
  46. }
  47. /**
  48. * Gets the time (milliseconds) to wait after user interaction before the camera starts rotating.
  49. */
  50. public get idleRotationWaitTime() {
  51. return this._idleRotationWaitTime;
  52. }
  53. /**
  54. * Sets the time (milliseconds) to take to spin up to the full idle rotation speed.
  55. */
  56. public set idleRotationSpinupTime(time: number) {
  57. this._idleRotationSpinupTime = time;
  58. }
  59. /**
  60. * Gets the time (milliseconds) to take to spin up to the full idle rotation speed.
  61. */
  62. public get idleRotationSpinupTime() {
  63. return this._idleRotationSpinupTime;
  64. }
  65. /**
  66. * Gets a value indicating if the camera is currently rotating because of this behavior
  67. */
  68. public get rotationInProgress(): boolean {
  69. return Math.abs(this._cameraRotationSpeed) > 0;
  70. }
  71. // Default behavior functions
  72. private _onPrePointerObservableObserver: Nullable<Observer<PointerInfoPre>>;
  73. private _onAfterCheckInputsObserver: Nullable<Observer<Camera>>;
  74. private _attachedCamera: Nullable<ArcRotateCamera>;
  75. private _isPointerDown = false;
  76. private _lastFrameTime: Nullable<number> = null;
  77. private _lastInteractionTime = -Infinity;
  78. private _cameraRotationSpeed: number = 0;
  79. /**
  80. * Initializes the behavior.
  81. */
  82. public init(): void {
  83. // Do notihng
  84. }
  85. /**
  86. * Attaches the behavior to its arc rotate camera.
  87. * @param camera Defines the camera to attach the behavior to
  88. */
  89. public attach(camera: ArcRotateCamera): void {
  90. this._attachedCamera = camera;
  91. let scene = this._attachedCamera.getScene();
  92. this._onPrePointerObservableObserver = scene.onPrePointerObservable.add((pointerInfoPre) => {
  93. if (pointerInfoPre.type === PointerEventTypes.POINTERDOWN) {
  94. this._isPointerDown = true;
  95. return;
  96. }
  97. if (pointerInfoPre.type === PointerEventTypes.POINTERUP) {
  98. this._isPointerDown = false;
  99. }
  100. });
  101. this._onAfterCheckInputsObserver = camera.onAfterCheckInputsObservable.add(() => {
  102. let now = Tools.Now;
  103. let dt = 0;
  104. if (this._lastFrameTime != null) {
  105. dt = now - this._lastFrameTime;
  106. }
  107. this._lastFrameTime = now;
  108. // Stop the animation if there is user interaction and the animation should stop for this interaction
  109. this._applyUserInteraction();
  110. let timeToRotation = now - this._lastInteractionTime - this._idleRotationWaitTime;
  111. let scale = Math.max(Math.min(timeToRotation / (this._idleRotationSpinupTime), 1), 0);
  112. this._cameraRotationSpeed = this._idleRotationSpeed * scale;
  113. // Step camera rotation by rotation speed
  114. if (this._attachedCamera) {
  115. this._attachedCamera.alpha -= this._cameraRotationSpeed * (dt / 1000);
  116. }
  117. });
  118. }
  119. /**
  120. * Detaches the behavior from its current arc rotate camera.
  121. */
  122. public detach(): void {
  123. if (!this._attachedCamera) {
  124. return;
  125. }
  126. let scene = this._attachedCamera.getScene();
  127. if (this._onPrePointerObservableObserver) {
  128. scene.onPrePointerObservable.remove(this._onPrePointerObservableObserver);
  129. }
  130. this._attachedCamera.onAfterCheckInputsObservable.remove(this._onAfterCheckInputsObserver);
  131. this._attachedCamera = null;
  132. }
  133. /**
  134. * Returns true if user is scrolling.
  135. * @return true if user is scrolling.
  136. */
  137. private _userIsZooming(): boolean {
  138. if (!this._attachedCamera) {
  139. return false;
  140. }
  141. return this._attachedCamera.inertialRadiusOffset !== 0;
  142. }
  143. private _lastFrameRadius = 0;
  144. private _shouldAnimationStopForInteraction(): boolean {
  145. if (!this._attachedCamera) {
  146. return false;
  147. }
  148. var zoomHasHitLimit = false;
  149. if (this._lastFrameRadius === this._attachedCamera.radius && this._attachedCamera.inertialRadiusOffset !== 0) {
  150. zoomHasHitLimit = true;
  151. }
  152. // Update the record of previous radius - works as an approx. indicator of hitting radius limits
  153. this._lastFrameRadius = this._attachedCamera.radius;
  154. return this._zoomStopsAnimation ? zoomHasHitLimit : this._userIsZooming();
  155. }
  156. /**
  157. * Applies any current user interaction to the camera. Takes into account maximum alpha rotation.
  158. */
  159. private _applyUserInteraction(): void {
  160. if (this._userIsMoving() && !this._shouldAnimationStopForInteraction()) {
  161. this._lastInteractionTime = Tools.Now;
  162. }
  163. }
  164. // Tools
  165. private _userIsMoving(): boolean {
  166. if (!this._attachedCamera) {
  167. return false;
  168. }
  169. return this._attachedCamera.inertialAlphaOffset !== 0 ||
  170. this._attachedCamera.inertialBetaOffset !== 0 ||
  171. this._attachedCamera.inertialRadiusOffset !== 0 ||
  172. this._attachedCamera.inertialPanningX !== 0 ||
  173. this._attachedCamera.inertialPanningY !== 0 ||
  174. this._isPointerDown;
  175. }
  176. }
  177. }