BaseCameraMouseWheelInput.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import { Nullable } from "../../types";
  2. import { serialize } from "../../Misc/decorators";
  3. import { EventState, Observable, Observer } from "../../Misc/observable";
  4. import { Camera } from "../../Cameras/camera";
  5. import { ICameraInput } from "../../Cameras/cameraInputsManager";
  6. import { PointerInfo, PointerEventTypes } from "../../Events/pointerEvents";
  7. /**
  8. * Base class for mouse wheel input..
  9. * See FollowCameraMouseWheelInput in src/Cameras/Inputs/freeCameraMouseWheelInput.ts
  10. * for example usage.
  11. */
  12. export abstract class BaseCameraMouseWheelInput implements ICameraInput<Camera> {
  13. /**
  14. * Defines the camera the input is attached to.
  15. */
  16. public abstract camera: Camera;
  17. /**
  18. * How fast is the camera moves in relation to X axis mouseWheel events.
  19. * Use negative value to reverse direction.
  20. */
  21. @serialize()
  22. public wheelPrecisionX = 3.0;
  23. /**
  24. * How fast is the camera moves in relation to Y axis mouseWheel events.
  25. * Use negative value to reverse direction.
  26. */
  27. @serialize()
  28. public wheelPrecisionY = 3.0;
  29. /**
  30. * How fast is the camera moves in relation to Z axis mouseWheel events.
  31. * Use negative value to reverse direction.
  32. */
  33. @serialize()
  34. public wheelPrecisionZ = 3.0;
  35. /**
  36. * Observable for when a mouse wheel move event occurs.
  37. */
  38. public onChangedObservable = new Observable<
  39. {wheelDeltaX: number, wheelDeltaY: number, wheelDeltaZ: number}>();
  40. private _wheel: Nullable<(pointer: PointerInfo, _: EventState) => void>;
  41. private _observer: Nullable<Observer<PointerInfo>>;
  42. /**
  43. * Attach the input controls to a specific dom element to get the input from.
  44. * @param element Defines the element the controls should be listened from
  45. * @param noPreventDefault Defines whether event caught by the controls
  46. * should call preventdefault().
  47. * (https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
  48. */
  49. public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
  50. this._wheel = (pointer, _) => {
  51. // sanity check - this should be a PointerWheel event.
  52. if (pointer.type !== PointerEventTypes.POINTERWHEEL) { return; }
  53. const event = <MouseWheelEvent>pointer.event;
  54. const platformScale =
  55. event.deltaMode === WheelEvent.DOM_DELTA_LINE ? this._ffMultiplier : 1;
  56. if (event.deltaY !== undefined) {
  57. // Most recent browsers versions have delta properties.
  58. // Firefox >= v17 (Has WebGL >= v4)
  59. // Chrome >= v31 (Has WebGL >= v8)
  60. // Edge >= v12 (Has WebGl >= v12)
  61. // https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
  62. this._wheelDeltaX +=
  63. this.wheelPrecisionX * platformScale * event.deltaX / this._normalize;
  64. this._wheelDeltaY -=
  65. this.wheelPrecisionY * platformScale * event.deltaY / this._normalize;
  66. this._wheelDeltaZ +=
  67. this.wheelPrecisionZ * platformScale * event.deltaZ / this._normalize;
  68. } else if ((<any>event).wheelDeltaY !== undefined) {
  69. // Unsure whether these catch anything more. Documentation
  70. // online is contradictory.
  71. this._wheelDeltaX +=
  72. this.wheelPrecisionX * platformScale *
  73. (<any>event).wheelDeltaX / this._normalize;
  74. this._wheelDeltaY -=
  75. this.wheelPrecisionY * platformScale *
  76. (<any>event).wheelDeltaY / this._normalize;
  77. this._wheelDeltaZ +=
  78. this.wheelPrecisionZ * platformScale *
  79. (<any>event).wheelDeltaZ / this._normalize;
  80. } else if ((<any>event).wheelDelta) {
  81. // IE >= v9 (Has WebGL >= v11)
  82. // Maybe others?
  83. this._wheelDeltaY -=
  84. this.wheelPrecisionY * (<any>event).wheelDelta / this._normalize;
  85. } else if (event.detail) {
  86. // Firefox < v17 (Has WebGL >= v4)
  87. // TODO How should we scale this?
  88. // Since it's Firefox, it's probably the same as
  89. // WheelEvent.DOM_DELTA_LINE.
  90. // ie: we can presume it needs scaled to match per-pixel.
  91. this._wheelDeltaY +=
  92. this.wheelPrecisionY * this._ffMultiplier * event.detail;
  93. if ("axis" in event &&
  94. (<any>event).axis === (<any>event).HORIZONTAL_AXIS) {
  95. this._wheelDeltaX = this._wheelDeltaY;
  96. this._wheelDeltaY = 0;
  97. }
  98. }
  99. if (event.preventDefault) {
  100. if (!noPreventDefault) {
  101. event.preventDefault();
  102. }
  103. }
  104. };
  105. this._observer = this.camera.getScene().onPointerObservable.add(
  106. this._wheel,
  107. PointerEventTypes.POINTERWHEEL);
  108. }
  109. /**
  110. * Detach the current controls from the specified dom element.
  111. * @param element Defines the element to stop listening the inputs from
  112. */
  113. public detachControl(element: Nullable<HTMLElement>): void {
  114. if (this._observer && element) {
  115. this.camera.getScene().onPointerObservable.remove(this._observer);
  116. this._observer = null;
  117. this._wheel = null;
  118. }
  119. if (this.onChangedObservable) {
  120. this.onChangedObservable.clear();
  121. }
  122. }
  123. /**
  124. * Called for each rendered frame.
  125. */
  126. public checkInputs(): void {
  127. this.onChangedObservable.notifyObservers({
  128. wheelDeltaX: this._wheelDeltaX,
  129. wheelDeltaY: this._wheelDeltaY,
  130. wheelDeltaZ: this._wheelDeltaZ
  131. });
  132. // Clear deltas.
  133. this._wheelDeltaX = 0;
  134. this._wheelDeltaY = 0;
  135. this._wheelDeltaZ = 0;
  136. }
  137. /**
  138. * Gets the class name of the current intput.
  139. * @returns the class name
  140. */
  141. public getClassName(): string {
  142. return "BaseCameraMouseWheelInput";
  143. }
  144. /**
  145. * Get the friendly name associated with the input class.
  146. * @returns the input friendly name
  147. */
  148. public getSimpleName(): string {
  149. return "mousewheel";
  150. }
  151. /**
  152. * Incremental value of multiple mouse wheel movements of the X axis.
  153. * Should be zero-ed when read.
  154. */
  155. protected _wheelDeltaX: number = 0;
  156. /**
  157. * Incremental value of multiple mouse wheel movements of the Y axis.
  158. * Should be zero-ed when read.
  159. */
  160. protected _wheelDeltaY: number = 0;
  161. /**
  162. * Incremental value of multiple mouse wheel movements of the Z axis.
  163. * Should be zero-ed when read.
  164. */
  165. protected _wheelDeltaZ: number = 0;
  166. /**
  167. * Firefox uses a different scheme to report scroll distances to other
  168. * browsers. Rather than use complicated methods to calculate the exact
  169. * multiple we need to apply, let's just cheat and use a constant.
  170. * https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/deltaMode
  171. * https://stackoverflow.com/questions/20110224/what-is-the-height-of-a-line-in-a-wheel-event-deltamode-dom-delta-line
  172. */
  173. private readonly _ffMultiplier = 12;
  174. /**
  175. * Different event attributes for wheel data fall into a few set ranges.
  176. * Some relevant but dated date here:
  177. * https://stackoverflow.com/questions/5527601/normalizing-mousewheel-speed-across-browsers
  178. */
  179. private readonly _normalize = 120;
  180. }