normalizeWheel.js.flow 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /**
  2. * Copyright (c) 2013-present, Facebook, Inc.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. *
  7. * @providesModule normalizeWheel
  8. * @typechecks
  9. */
  10. 'use strict';
  11. const UserAgent = require("./UserAgent");
  12. const isEventSupported = require("./isEventSupported"); // Reasonable defaults
  13. const PIXEL_STEP = 10;
  14. const LINE_HEIGHT = 40;
  15. const PAGE_HEIGHT = 800;
  16. /**
  17. * Mouse wheel (and 2-finger trackpad) support on the web sucks. It is
  18. * complicated, thus this doc is long and (hopefully) detailed enough to answer
  19. * your questions.
  20. *
  21. * If you need to react to the mouse wheel in a predictable way, this code is
  22. * like your bestest friend. * hugs *
  23. *
  24. * As of today, there are 4 DOM event types you can listen to:
  25. *
  26. * 'wheel' -- Chrome(31+), FF(17+), IE(9+)
  27. * 'mousewheel' -- Chrome, IE(6+), Opera, Safari
  28. * 'MozMousePixelScroll' -- FF(3.5 only!) (2010-2013) -- don't bother!
  29. * 'DOMMouseScroll' -- FF(0.9.7+) since 2003
  30. *
  31. * So what to do? The is the best:
  32. *
  33. * normalizeWheel.getEventType();
  34. *
  35. * In your event callback, use this code to get sane interpretation of the
  36. * deltas. This code will return an object with properties:
  37. *
  38. * spinX -- normalized spin speed (use for zoom) - x plane
  39. * spinY -- " - y plane
  40. * pixelX -- normalized distance (to pixels) - x plane
  41. * pixelY -- " - y plane
  42. *
  43. * Wheel values are provided by the browser assuming you are using the wheel to
  44. * scroll a web page by a number of lines or pixels (or pages). Values can vary
  45. * significantly on different platforms and browsers, forgetting that you can
  46. * scroll at different speeds. Some devices (like trackpads) emit more events
  47. * at smaller increments with fine granularity, and some emit massive jumps with
  48. * linear speed or acceleration.
  49. *
  50. * This code does its best to normalize the deltas for you:
  51. *
  52. * - spin is trying to normalize how far the wheel was spun (or trackpad
  53. * dragged). This is super useful for zoom support where you want to
  54. * throw away the chunky scroll steps on the PC and make those equal to
  55. * the slow and smooth tiny steps on the Mac. Key data: This code tries to
  56. * resolve a single slow step on a wheel to 1.
  57. *
  58. * - pixel is normalizing the desired scroll delta in pixel units. You'll
  59. * get the crazy differences between browsers, but at least it'll be in
  60. * pixels!
  61. *
  62. * - positive value indicates scrolling DOWN/RIGHT, negative UP/LEFT. This
  63. * should translate to positive value zooming IN, negative zooming OUT.
  64. * This matches the newer 'wheel' event.
  65. *
  66. * Why are there spinX, spinY (or pixels)?
  67. *
  68. * - spinX is a 2-finger side drag on the trackpad, and a shift + wheel turn
  69. * with a mouse. It results in side-scrolling in the browser by default.
  70. *
  71. * - spinY is what you expect -- it's the classic axis of a mouse wheel.
  72. *
  73. * - I dropped spinZ/pixelZ. It is supported by the DOM 3 'wheel' event and
  74. * probably is by browsers in conjunction with fancy 3D controllers .. but
  75. * you know.
  76. *
  77. * Implementation info:
  78. *
  79. * Examples of 'wheel' event if you scroll slowly (down) by one step with an
  80. * average mouse:
  81. *
  82. * OS X + Chrome (mouse) - 4 pixel delta (wheelDelta -120)
  83. * OS X + Safari (mouse) - N/A pixel delta (wheelDelta -12)
  84. * OS X + Firefox (mouse) - 0.1 line delta (wheelDelta N/A)
  85. * Win8 + Chrome (mouse) - 100 pixel delta (wheelDelta -120)
  86. * Win8 + Firefox (mouse) - 3 line delta (wheelDelta -120)
  87. *
  88. * On the trackpad:
  89. *
  90. * OS X + Chrome (trackpad) - 2 pixel delta (wheelDelta -6)
  91. * OS X + Firefox (trackpad) - 1 pixel delta (wheelDelta N/A)
  92. *
  93. * On other/older browsers.. it's more complicated as there can be multiple and
  94. * also missing delta values.
  95. *
  96. * The 'wheel' event is more standard:
  97. *
  98. * http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents
  99. *
  100. * The basics is that it includes a unit, deltaMode (pixels, lines, pages), and
  101. * deltaX, deltaY and deltaZ. Some browsers provide other values to maintain
  102. * backward compatibility with older events. Those other values help us
  103. * better normalize spin speed. Example of what the browsers provide:
  104. *
  105. * | event.wheelDelta | event.detail
  106. * ------------------+------------------+--------------
  107. * Safari v5/OS X | -120 | 0
  108. * Safari v5/Win7 | -120 | 0
  109. * Chrome v17/OS X | -120 | 0
  110. * Chrome v17/Win7 | -120 | 0
  111. * IE9/Win7 | -120 | undefined
  112. * Firefox v4/OS X | undefined | 1
  113. * Firefox v4/Win7 | undefined | 3
  114. *
  115. */
  116. function normalizeWheel(
  117. /*object*/
  118. event)
  119. /*object*/
  120. {
  121. let sX = 0,
  122. sY = 0,
  123. // spinX, spinY
  124. pX = 0,
  125. pY = 0; // pixelX, pixelY
  126. // Legacy
  127. if ('detail' in event) {
  128. sY = event.detail;
  129. }
  130. if ('wheelDelta' in event) {
  131. sY = -event.wheelDelta / 120;
  132. }
  133. if ('wheelDeltaY' in event) {
  134. sY = -event.wheelDeltaY / 120;
  135. }
  136. if ('wheelDeltaX' in event) {
  137. sX = -event.wheelDeltaX / 120;
  138. } // side scrolling on FF with DOMMouseScroll
  139. if ('axis' in event && event.axis === event.HORIZONTAL_AXIS) {
  140. sX = sY;
  141. sY = 0;
  142. }
  143. pX = sX * PIXEL_STEP;
  144. pY = sY * PIXEL_STEP;
  145. if ('deltaY' in event) {
  146. pY = event.deltaY;
  147. }
  148. if ('deltaX' in event) {
  149. pX = event.deltaX;
  150. }
  151. if ((pX || pY) && event.deltaMode) {
  152. if (event.deltaMode == 1) {
  153. // delta in LINE units
  154. pX *= LINE_HEIGHT;
  155. pY *= LINE_HEIGHT;
  156. } else {
  157. // delta in PAGE units
  158. pX *= PAGE_HEIGHT;
  159. pY *= PAGE_HEIGHT;
  160. }
  161. } // Fall-back if spin cannot be determined
  162. if (pX && !sX) {
  163. sX = pX < 1 ? -1 : 1;
  164. }
  165. if (pY && !sY) {
  166. sY = pY < 1 ? -1 : 1;
  167. }
  168. return {
  169. spinX: sX,
  170. spinY: sY,
  171. pixelX: pX,
  172. pixelY: pY
  173. };
  174. }
  175. /**
  176. * The best combination if you prefer spinX + spinY normalization. It favors
  177. * the older DOMMouseScroll for Firefox, as FF does not include wheelDelta with
  178. * 'wheel' event, making spin speed determination impossible.
  179. */
  180. normalizeWheel.getEventType = function ()
  181. /*string*/
  182. {
  183. return UserAgent.isBrowser('Firefox') ? 'DOMMouseScroll' : isEventSupported('wheel') ? 'wheel' : 'mousewheel';
  184. };
  185. module.exports = normalizeWheel;