mouse-wheel.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. /*!
  2. * better-scroll / mouse-wheel
  3. * (c) 2016-2021 ustbhuangyi
  4. * Released under the MIT License.
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  8. typeof define === 'function' && define.amd ? define(factory) :
  9. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.MouseWheel = factory());
  10. }(this, (function () { 'use strict';
  11. function warn(msg) {
  12. console.error("[BScroll warn]: " + msg);
  13. }
  14. // ssr support
  15. var inBrowser = typeof window !== 'undefined';
  16. var ua = inBrowser && navigator.userAgent.toLowerCase();
  17. !!(ua && /wechatdevtools/.test(ua));
  18. ua && ua.indexOf('android') > 0;
  19. /* istanbul ignore next */
  20. ((function () {
  21. if (typeof ua === 'string') {
  22. var regex = /os (\d\d?_\d(_\d)?)/;
  23. var matches = regex.exec(ua);
  24. if (!matches)
  25. return false;
  26. var parts = matches[1].split('_').map(function (item) {
  27. return parseInt(item, 10);
  28. });
  29. // ios version >= 13.4 issue 982
  30. return !!(parts[0] === 13 && parts[1] >= 4);
  31. }
  32. return false;
  33. }))();
  34. /* istanbul ignore next */
  35. var supportsPassive = false;
  36. /* istanbul ignore next */
  37. if (inBrowser) {
  38. var EventName = 'test-passive';
  39. try {
  40. var opts = {};
  41. Object.defineProperty(opts, 'passive', {
  42. get: function () {
  43. supportsPassive = true;
  44. },
  45. }); // https://github.com/facebook/flow/issues/285
  46. window.addEventListener(EventName, function () { }, opts);
  47. }
  48. catch (e) { }
  49. }
  50. var extend = function (target, source) {
  51. for (var key in source) {
  52. target[key] = source[key];
  53. }
  54. return target;
  55. };
  56. var elementStyle = (inBrowser &&
  57. document.createElement('div').style);
  58. var vendor = (function () {
  59. /* istanbul ignore if */
  60. if (!inBrowser) {
  61. return false;
  62. }
  63. var transformNames = [
  64. {
  65. key: 'standard',
  66. value: 'transform',
  67. },
  68. {
  69. key: 'webkit',
  70. value: 'webkitTransform',
  71. },
  72. {
  73. key: 'Moz',
  74. value: 'MozTransform',
  75. },
  76. {
  77. key: 'O',
  78. value: 'OTransform',
  79. },
  80. {
  81. key: 'ms',
  82. value: 'msTransform',
  83. },
  84. ];
  85. for (var _i = 0, transformNames_1 = transformNames; _i < transformNames_1.length; _i++) {
  86. var obj = transformNames_1[_i];
  87. if (elementStyle[obj.value] !== undefined) {
  88. return obj.key;
  89. }
  90. }
  91. /* istanbul ignore next */
  92. return false;
  93. })();
  94. /* istanbul ignore next */
  95. function prefixStyle(style) {
  96. if (vendor === false) {
  97. return style;
  98. }
  99. if (vendor === 'standard') {
  100. if (style === 'transitionEnd') {
  101. return 'transitionend';
  102. }
  103. return style;
  104. }
  105. return vendor + style.charAt(0).toUpperCase() + style.substr(1);
  106. }
  107. function addEvent(el, type, fn, capture) {
  108. var useCapture = supportsPassive
  109. ? {
  110. passive: false,
  111. capture: !!capture,
  112. }
  113. : !!capture;
  114. el.addEventListener(type, fn, useCapture);
  115. }
  116. function removeEvent(el, type, fn, capture) {
  117. el.removeEventListener(type, fn, {
  118. capture: !!capture,
  119. });
  120. }
  121. vendor && vendor !== 'standard' ? '-' + vendor.toLowerCase() + '-' : '';
  122. var transform = prefixStyle('transform');
  123. var transition = prefixStyle('transition');
  124. inBrowser && prefixStyle('perspective') in elementStyle;
  125. ({
  126. transform: transform,
  127. transition: transition,
  128. transitionTimingFunction: prefixStyle('transitionTimingFunction'),
  129. transitionDuration: prefixStyle('transitionDuration'),
  130. transitionDelay: prefixStyle('transitionDelay'),
  131. transformOrigin: prefixStyle('transformOrigin'),
  132. transitionEnd: prefixStyle('transitionEnd'),
  133. transitionProperty: prefixStyle('transitionProperty'),
  134. });
  135. function preventDefaultExceptionFn(el, exceptions) {
  136. for (var i in exceptions) {
  137. if (exceptions[i].test(el[i])) {
  138. return true;
  139. }
  140. }
  141. return false;
  142. }
  143. var EventRegister = /** @class */ (function () {
  144. function EventRegister(wrapper, events) {
  145. this.wrapper = wrapper;
  146. this.events = events;
  147. this.addDOMEvents();
  148. }
  149. EventRegister.prototype.destroy = function () {
  150. this.removeDOMEvents();
  151. this.events = [];
  152. };
  153. EventRegister.prototype.addDOMEvents = function () {
  154. this.handleDOMEvents(addEvent);
  155. };
  156. EventRegister.prototype.removeDOMEvents = function () {
  157. this.handleDOMEvents(removeEvent);
  158. };
  159. EventRegister.prototype.handleDOMEvents = function (eventOperation) {
  160. var _this = this;
  161. var wrapper = this.wrapper;
  162. this.events.forEach(function (event) {
  163. eventOperation(wrapper, event.name, _this, !!event.capture);
  164. });
  165. };
  166. EventRegister.prototype.handleEvent = function (e) {
  167. var eventType = e.type;
  168. this.events.some(function (event) {
  169. if (event.name === eventType) {
  170. event.handler(e);
  171. return true;
  172. }
  173. return false;
  174. });
  175. };
  176. return EventRegister;
  177. }());
  178. var MouseWheel = /** @class */ (function () {
  179. function MouseWheel(scroll) {
  180. this.scroll = scroll;
  181. this.wheelEndTimer = 0;
  182. this.wheelMoveTimer = 0;
  183. this.wheelStart = false;
  184. this.init();
  185. }
  186. MouseWheel.prototype.init = function () {
  187. this.handleBScroll();
  188. this.handleOptions();
  189. this.handleHooks();
  190. this.registerEvent();
  191. };
  192. MouseWheel.prototype.handleBScroll = function () {
  193. this.scroll.registerType([
  194. 'alterOptions',
  195. 'mousewheelStart',
  196. 'mousewheelMove',
  197. 'mousewheelEnd',
  198. ]);
  199. };
  200. MouseWheel.prototype.handleOptions = function () {
  201. var userOptions = (this.scroll.options.mouseWheel === true
  202. ? {}
  203. : this.scroll.options.mouseWheel);
  204. var defaultOptions = {
  205. speed: 20,
  206. invert: false,
  207. easeTime: 300,
  208. discreteTime: 400,
  209. throttleTime: 0,
  210. dampingFactor: 0.1,
  211. };
  212. this.mouseWheelOpt = extend(defaultOptions, userOptions);
  213. };
  214. MouseWheel.prototype.handleHooks = function () {
  215. this.hooksFn = [];
  216. this.registerHooks(this.scroll.hooks, 'destroy', this.destroy);
  217. };
  218. MouseWheel.prototype.registerEvent = function () {
  219. this.eventRegister = new EventRegister(this.scroll.scroller.wrapper, [
  220. {
  221. name: 'wheel',
  222. handler: this.wheelHandler.bind(this),
  223. },
  224. {
  225. name: 'mousewheel',
  226. handler: this.wheelHandler.bind(this),
  227. },
  228. {
  229. name: 'DOMMouseScroll',
  230. handler: this.wheelHandler.bind(this),
  231. },
  232. ]);
  233. };
  234. MouseWheel.prototype.registerHooks = function (hooks, name, handler) {
  235. hooks.on(name, handler, this);
  236. this.hooksFn.push([hooks, name, handler]);
  237. };
  238. MouseWheel.prototype.wheelHandler = function (e) {
  239. if (!this.scroll.enabled) {
  240. return;
  241. }
  242. this.beforeHandler(e);
  243. // start
  244. if (!this.wheelStart) {
  245. this.wheelStartHandler(e);
  246. this.wheelStart = true;
  247. }
  248. // move
  249. var delta = this.getWheelDelta(e);
  250. this.wheelMoveHandler(delta);
  251. // end
  252. this.wheelEndDetector(delta);
  253. };
  254. MouseWheel.prototype.wheelStartHandler = function (e) {
  255. this.cleanCache();
  256. var _a = this.scroll.scroller, scrollBehaviorX = _a.scrollBehaviorX, scrollBehaviorY = _a.scrollBehaviorY;
  257. scrollBehaviorX.setMovingDirection(0 /* Default */);
  258. scrollBehaviorY.setMovingDirection(0 /* Default */);
  259. scrollBehaviorX.setDirection(0 /* Default */);
  260. scrollBehaviorY.setDirection(0 /* Default */);
  261. this.scroll.trigger(this.scroll.eventTypes.alterOptions, this.mouseWheelOpt);
  262. this.scroll.trigger(this.scroll.eventTypes.mousewheelStart);
  263. };
  264. MouseWheel.prototype.cleanCache = function () {
  265. this.deltaCache = [];
  266. };
  267. MouseWheel.prototype.wheelMoveHandler = function (delta) {
  268. var _this = this;
  269. var _a = this.mouseWheelOpt, throttleTime = _a.throttleTime, dampingFactor = _a.dampingFactor;
  270. if (throttleTime && this.wheelMoveTimer) {
  271. this.deltaCache.push(delta);
  272. }
  273. else {
  274. var cachedDelta = this.deltaCache.reduce(function (prev, current) {
  275. return {
  276. x: prev.x + current.x,
  277. y: prev.y + current.y,
  278. };
  279. }, { x: 0, y: 0 });
  280. this.cleanCache();
  281. var _b = this.scroll.scroller, scrollBehaviorX = _b.scrollBehaviorX, scrollBehaviorY = _b.scrollBehaviorY;
  282. scrollBehaviorX.setMovingDirection(-delta.directionX);
  283. scrollBehaviorY.setMovingDirection(-delta.directionY);
  284. scrollBehaviorX.setDirection(delta.x);
  285. scrollBehaviorY.setDirection(delta.y);
  286. // when out of boundary, perform a damping scroll
  287. var newX = scrollBehaviorX.performDampingAlgorithm(Math.round(delta.x) + cachedDelta.x, dampingFactor);
  288. var newY = scrollBehaviorY.performDampingAlgorithm(Math.round(delta.y) + cachedDelta.x, dampingFactor);
  289. if (!this.scroll.trigger(this.scroll.eventTypes.mousewheelMove, {
  290. x: newX,
  291. y: newY,
  292. })) {
  293. var easeTime = this.getEaseTime();
  294. if (newX !== this.scroll.x || newY !== this.scroll.y) {
  295. this.scroll.scrollTo(newX, newY, easeTime);
  296. }
  297. }
  298. if (throttleTime) {
  299. this.wheelMoveTimer = window.setTimeout(function () {
  300. _this.wheelMoveTimer = 0;
  301. }, throttleTime);
  302. }
  303. }
  304. };
  305. MouseWheel.prototype.wheelEndDetector = function (delta) {
  306. var _this = this;
  307. window.clearTimeout(this.wheelEndTimer);
  308. this.wheelEndTimer = window.setTimeout(function () {
  309. _this.wheelStart = false;
  310. window.clearTimeout(_this.wheelMoveTimer);
  311. _this.wheelMoveTimer = 0;
  312. _this.scroll.trigger(_this.scroll.eventTypes.mousewheelEnd, delta);
  313. }, this.mouseWheelOpt.discreteTime);
  314. };
  315. MouseWheel.prototype.getWheelDelta = function (e) {
  316. var _a = this.mouseWheelOpt, speed = _a.speed, invert = _a.invert;
  317. var wheelDeltaX = 0;
  318. var wheelDeltaY = 0;
  319. var direction = invert ? -1 /* Negative */ : 1 /* Positive */;
  320. switch (true) {
  321. case 'deltaX' in e:
  322. if (e.deltaMode === 1) {
  323. wheelDeltaX = -e.deltaX * speed;
  324. wheelDeltaY = -e.deltaY * speed;
  325. }
  326. else {
  327. wheelDeltaX = -e.deltaX;
  328. wheelDeltaY = -e.deltaY;
  329. }
  330. break;
  331. case 'wheelDeltaX' in e:
  332. wheelDeltaX = (e.wheelDeltaX / 120) * speed;
  333. wheelDeltaY = (e.wheelDeltaY / 120) * speed;
  334. break;
  335. case 'wheelDelta' in e:
  336. wheelDeltaX = wheelDeltaY = (e.wheelDelta / 120) * speed;
  337. break;
  338. case 'detail' in e:
  339. wheelDeltaX = wheelDeltaY = (-e.detail / 3) * speed;
  340. break;
  341. }
  342. wheelDeltaX *= direction;
  343. wheelDeltaY *= direction;
  344. if (!this.scroll.hasVerticalScroll) {
  345. if (Math.abs(wheelDeltaY) > Math.abs(wheelDeltaX)) {
  346. wheelDeltaX = wheelDeltaY;
  347. }
  348. wheelDeltaY = 0;
  349. }
  350. if (!this.scroll.hasHorizontalScroll) {
  351. wheelDeltaX = 0;
  352. }
  353. var directionX = wheelDeltaX > 0
  354. ? -1 /* Negative */
  355. : wheelDeltaX < 0
  356. ? 1 /* Positive */
  357. : 0 /* Default */;
  358. var directionY = wheelDeltaY > 0
  359. ? -1 /* Negative */
  360. : wheelDeltaY < 0
  361. ? 1 /* Positive */
  362. : 0 /* Default */;
  363. return {
  364. x: wheelDeltaX,
  365. y: wheelDeltaY,
  366. directionX: directionX,
  367. directionY: directionY,
  368. };
  369. };
  370. MouseWheel.prototype.beforeHandler = function (e) {
  371. var _a = this.scroll.options, preventDefault = _a.preventDefault, stopPropagation = _a.stopPropagation, preventDefaultException = _a.preventDefaultException;
  372. if (preventDefault &&
  373. !preventDefaultExceptionFn(e.target, preventDefaultException)) {
  374. e.preventDefault();
  375. }
  376. if (stopPropagation) {
  377. e.stopPropagation();
  378. }
  379. };
  380. MouseWheel.prototype.getEaseTime = function () {
  381. var SAFE_EASETIME = 100;
  382. var easeTime = this.mouseWheelOpt.easeTime;
  383. // scrollEnd event will be triggered in every calling of scrollTo when easeTime is too small
  384. // easeTime needs to be greater than 100
  385. if (easeTime < SAFE_EASETIME) {
  386. warn("easeTime should be greater than 100." +
  387. "If mouseWheel easeTime is too small," +
  388. "scrollEnd will be triggered many times.");
  389. }
  390. return Math.max(easeTime, SAFE_EASETIME);
  391. };
  392. MouseWheel.prototype.destroy = function () {
  393. this.eventRegister.destroy();
  394. window.clearTimeout(this.wheelEndTimer);
  395. window.clearTimeout(this.wheelMoveTimer);
  396. this.hooksFn.forEach(function (item) {
  397. var hooks = item[0];
  398. var hooksName = item[1];
  399. var handlerFn = item[2];
  400. hooks.off(hooksName, handlerFn);
  401. });
  402. };
  403. MouseWheel.pluginName = 'mouseWheel';
  404. MouseWheel.applyOrder = "pre" /* Pre */;
  405. return MouseWheel;
  406. }());
  407. return MouseWheel;
  408. })));