jquery-asHoverScroll.es.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. /**
  2. * jQuery asHoverScroll v0.3.7
  3. * https://github.com/amazingSurge/jquery-asHoverScroll
  4. *
  5. * Copyright (c) amazingSurge
  6. * Released under the LGPL-3.0 license
  7. */
  8. import $$1 from 'jquery';
  9. var DEFAULTS = {
  10. namespace: 'asHoverScroll',
  11. list: '> ul',
  12. item: '> li',
  13. exception: null,
  14. direction: 'vertical',
  15. fixed: false,
  16. mouseMove: true,
  17. touchScroll: true,
  18. pointerScroll: true,
  19. useCssTransforms: true,
  20. useCssTransforms3d: true,
  21. boundary: 10,
  22. throttle: 20,
  23. // callbacks
  24. onEnter() {
  25. $(this).siblings().removeClass('is-active');
  26. $(this).addClass('is-active');
  27. },
  28. onLeave() {
  29. $(this).removeClass('is-active');
  30. }
  31. };
  32. /**
  33. * Css features detect
  34. **/
  35. let support = {};
  36. ((support) => {
  37. /**
  38. * Borrowed from Owl carousel
  39. **/
  40. 'use strict';
  41. let events = {
  42. transition: {
  43. end: {
  44. WebkitTransition: 'webkitTransitionEnd',
  45. MozTransition: 'transitionend',
  46. OTransition: 'oTransitionEnd',
  47. transition: 'transitionend'
  48. }
  49. },
  50. animation: {
  51. end: {
  52. WebkitAnimation: 'webkitAnimationEnd',
  53. MozAnimation: 'animationend',
  54. OAnimation: 'oAnimationEnd',
  55. animation: 'animationend'
  56. }
  57. }
  58. },
  59. prefixes = ['webkit', 'Moz', 'O', 'ms'],
  60. style = $$1('<support>').get(0).style,
  61. tests = {
  62. csstransforms() {
  63. return Boolean(test('transform'));
  64. },
  65. csstransforms3d() {
  66. return Boolean(test('perspective'));
  67. },
  68. csstransitions() {
  69. return Boolean(test('transition'));
  70. },
  71. cssanimations() {
  72. return Boolean(test('animation'));
  73. }
  74. };
  75. let test = (property, prefixed) => {
  76. let result = false,
  77. upper = property.charAt(0).toUpperCase() + property.slice(1);
  78. if (style[property] !== undefined) {
  79. result = property;
  80. }
  81. if (!result) {
  82. $$1.each(prefixes, (i, prefix) => {
  83. if (style[prefix + upper] !== undefined) {
  84. result = `-${prefix.toLowerCase()}-${upper}`;
  85. return false;
  86. }
  87. return true;
  88. });
  89. }
  90. if (prefixed) {
  91. return result;
  92. }
  93. if (result) {
  94. return true;
  95. }
  96. return false;
  97. };
  98. let prefixed = (property) => {
  99. return test(property, true);
  100. };
  101. if (tests.csstransitions()) {
  102. /*eslint no-new-wrappers: "off"*/
  103. support.transition = new String(prefixed('transition'));
  104. support.transition.end = events.transition.end[support.transition];
  105. }
  106. if (tests.cssanimations()) {
  107. /*eslint no-new-wrappers: "off"*/
  108. support.animation = new String(prefixed('animation'));
  109. support.animation.end = events.animation.end[support.animation];
  110. }
  111. if (tests.csstransforms()) {
  112. /*eslint no-new-wrappers: "off"*/
  113. support.transform = new String(prefixed('transform'));
  114. support.transform3d = tests.csstransforms3d();
  115. }
  116. if (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch) {
  117. support.touch = true;
  118. } else {
  119. support.touch = false;
  120. }
  121. if (window.PointerEvent || window.MSPointerEvent) {
  122. support.pointer = true;
  123. } else {
  124. support.pointer = false;
  125. }
  126. support.convertMatrixToArray = function(value) {
  127. if (value && (value.substr(0, 6) === "matrix")) {
  128. return value.replace(/^.*\((.*)\)$/g, "$1").replace(/px/g, '').split(/, +/);
  129. }
  130. return false;
  131. };
  132. support.prefixPointerEvent = (pointerEvent) => {
  133. let charStart = 9,
  134. subStart = 10;
  135. return window.MSPointerEvent ?
  136. `MSPointer${pointerEvent.charAt(charStart).toUpperCase()}${pointerEvent.substr(subStart)}` :
  137. pointerEvent;
  138. };
  139. })(support);
  140. const NAMESPACE$1 = 'asHoverScroll';
  141. let instanceId = 0;
  142. /**
  143. * Plugin constructor
  144. **/
  145. class asHoverScroll {
  146. constructor(element, options) {
  147. this.element = element;
  148. this.$element = $$1(element);
  149. this.options = $$1.extend({}, DEFAULTS, options, this.$element.data());
  150. this.$list = $$1(this.options.list, this.$element);
  151. this.classes = {
  152. disabled: `${this.options.namespace}-disabled`
  153. };
  154. if (this.options.direction === 'vertical') {
  155. this.attributes = {
  156. page: 'pageY',
  157. axis: 'Y',
  158. position: 'top',
  159. length: 'height',
  160. offset: 'offsetTop',
  161. client: 'clientY',
  162. clientLength: 'clientHeight'
  163. };
  164. } else if (this.options.direction === 'horizontal') {
  165. this.attributes = {
  166. page: 'pageX',
  167. axis: 'X',
  168. position: 'left',
  169. length: 'width',
  170. offset: 'offsetLeft',
  171. client: 'clientX',
  172. clientLength: 'clientWidth'
  173. };
  174. }
  175. // Current state information.
  176. this._states = {};
  177. // Current state information for the touch operation.
  178. this._scroll = {
  179. time: null,
  180. pointer: null
  181. };
  182. this.instanceId = (++instanceId);
  183. this.trigger('init');
  184. this.init();
  185. }
  186. init() {
  187. this.initPosition();
  188. // init length data
  189. this.updateLength();
  190. this.bindEvents();
  191. }
  192. bindEvents() {
  193. const that = this;
  194. const enterEvents = ['enter'];
  195. const leaveEvents = [];
  196. if (this.options.mouseMove) {
  197. this.$element.on(this.eventName('mousemove'), $$1.proxy(this.onMove, this));
  198. enterEvents.push('mouseenter');
  199. leaveEvents.push('mouseleave');
  200. }
  201. if (this.options.touchScroll && support.touch) {
  202. this.$element.on(this.eventName('touchstart'), $$1.proxy(this.onScrollStart, this));
  203. this.$element.on(this.eventName('touchcancel'), $$1.proxy(this.onScrollEnd, this));
  204. }
  205. if (this.options.pointerScroll && support.pointer) {
  206. this.$element.on(this.eventName(support.prefixPointerEvent('pointerdown')), $$1.proxy(this.onScrollStart, this));
  207. // fixed by FreMaNgo
  208. // this.$element.on(this.eventName(support.prefixPointerEvent('pointerdown')),(e) => {
  209. // let isUp = false;
  210. // this.$element.one('pointerup', () => {
  211. // isUp = true;
  212. // });
  213. // window.setTimeout(() => {
  214. // if(isUp){
  215. // return false;
  216. // }else{
  217. // this.$element.off('pointerup');
  218. // $.proxy(this.onScrollStart, this)(e);
  219. // }
  220. // }, 100)
  221. // });
  222. // fixed by FreMaNgo -- END
  223. this.$element.on(this.eventName(support.prefixPointerEvent('pointercancel')), $$1.proxy(this.onScrollEnd, this));
  224. }
  225. this.$list.on(this.eventName(enterEvents.join(' ')), this.options.item, () => {
  226. if (!that.is('scrolling')) {
  227. that.options.onEnter.call(this);
  228. }
  229. });
  230. this.$list.on(this.eventName(leaveEvents.join(' ')), this.options.item, () => {
  231. if (!that.is('scrolling')) {
  232. that.options.onLeave.call(this);
  233. }
  234. });
  235. $$1(window).on(this.eventNameWithId('orientationchange'), () => {
  236. that.update();
  237. });
  238. $$1(window).on(this.eventNameWithId('resize'), this.throttle(() => {
  239. that.update();
  240. }, this.options.throttle));
  241. }
  242. unbindEvents() {
  243. this.$element.off(this.eventName());
  244. this.$list.off(this.eventName());
  245. $$1(window).off(this.eventNameWithId());
  246. }
  247. /**
  248. * Handles `touchstart` and `mousedown` events.
  249. */
  250. onScrollStart(event) {
  251. const that = this;
  252. if (this.is('scrolling')) {
  253. return;
  254. }
  255. if (event.which === 3) {
  256. return;
  257. }
  258. if ($$1(event.target).closest(this.options.exception).length > 0) {
  259. return;
  260. }
  261. this._scroll.time = new Date().getTime();
  262. this._scroll.pointer = this.pointer(event);
  263. this._scroll.start = this.getPosition();
  264. this._scroll.moved = false;
  265. const callback = () => {
  266. this.enter('scrolling');
  267. this.trigger('scroll');
  268. };
  269. if (this.options.touchScroll && support.touch) {
  270. $$1(document).on(this.eventName('touchend'), $$1.proxy(this.onScrollEnd, this));
  271. $$1(document).one(this.eventName('touchmove'), $$1.proxy(function() {
  272. if(!this.is('scrolling')){
  273. $$1(document).on(that.eventName('touchmove'), $$1.proxy(this.onScrollMove, this));
  274. callback();
  275. }
  276. }, this));
  277. }
  278. if (this.options.pointerScroll && support.pointer) {
  279. $$1(document).on(this.eventName(support.prefixPointerEvent('pointerup')), $$1.proxy(this.onScrollEnd, this));
  280. $$1(document).one(this.eventName(support.prefixPointerEvent('pointermove')), $$1.proxy(function() {
  281. if(!this.is('scrolling')){
  282. $$1(document).on(that.eventName(support.prefixPointerEvent('pointermove')), $$1.proxy(this.onScrollMove, this));
  283. callback();
  284. }
  285. }, this));
  286. }
  287. $$1(document).on(this.eventName('blur'), $$1.proxy(this.onScrollEnd, this));
  288. event.preventDefault();
  289. }
  290. /**
  291. * Handles the `touchmove` and `mousemove` events.
  292. */
  293. onScrollMove(event) {
  294. this._scroll.updated = this.pointer(event);
  295. const distance = this.distance(this._scroll.pointer, this._scroll.updated);
  296. if (Math.abs(this._scroll.pointer.x - this._scroll.updated.x) > 10 || Math.abs(this._scroll.pointer.y - this._scroll.updated.y) > 10) {
  297. this._scroll.moved = true;
  298. }
  299. if (!this.is('scrolling')) {
  300. return;
  301. }
  302. event.preventDefault();
  303. let postion = this._scroll.start + distance;
  304. if (this.canScroll()) {
  305. if (postion > 0) {
  306. postion = 0;
  307. } else if (postion < this.containerLength - this.listLength) {
  308. postion = this.containerLength - this.listLength;
  309. }
  310. this.updatePosition(postion);
  311. }
  312. }
  313. /**
  314. * Handles the `touchend` and `mouseup` events.
  315. */
  316. onScrollEnd(event) {
  317. if (!this._scroll.moved) {
  318. $$1(event.target).trigger('tap');
  319. }
  320. // if (!this.is('scrolling')) {
  321. // return;
  322. // }
  323. if (this.options.touchScroll && support.touch) {
  324. $$1(document).off(this.eventName('touchmove touchend'));
  325. }
  326. if (this.options.pointerScroll && support.pointer) {
  327. $$1(document).off(this.eventName(support.prefixPointerEvent('pointermove pointerup')));
  328. }
  329. $$1(document).off(this.eventName('blur'));
  330. // touch will trigger mousemove event after 300ms delay. So we need avoid it
  331. // setTimeout(() => {
  332. this.leave('scrolling');
  333. this.trigger('scrolled');
  334. // }, 500);
  335. }
  336. /**
  337. * Gets unified pointer coordinates from event.
  338. * @returns {Object} - Contains `x` and `y` coordinates of current pointer position.
  339. */
  340. pointer(event) {
  341. const result = {
  342. x: null,
  343. y: null
  344. };
  345. event = this.getEvent(event);
  346. if (event.pageX && !this.options.fixed) {
  347. result.x = event.pageX;
  348. result.y = event.pageY;
  349. } else {
  350. result.x = event.clientX;
  351. result.y = event.clientY;
  352. }
  353. return result;
  354. }
  355. getEvent(event) {
  356. event = event.originalEvent || event || window.event;
  357. event = event.touches && event.touches.length ?
  358. event.touches[0] : event.changedTouches && event.changedTouches.length ?
  359. event.changedTouches[0] : event;
  360. return event;
  361. }
  362. /**
  363. * Gets the distance of two pointer.
  364. */
  365. distance(first, second) {
  366. if (this.options.direction === 'vertical') {
  367. return second.y - first.y;
  368. }
  369. return second.x - first.x;
  370. }
  371. onMove(event) {
  372. event = this.getEvent(event);
  373. if (this.is('scrolling')) {
  374. return;
  375. }
  376. if (this.isMatchScroll(event)) {
  377. let pointer;
  378. let distance;
  379. let offset;
  380. if (event[this.attributes.page] && !this.options.fixed) {
  381. pointer = event[this.attributes.page];
  382. } else {
  383. pointer = event[this.attributes.client];
  384. }
  385. offset = pointer - this.element[this.attributes.offset];
  386. if (offset < this.options.boundary) {
  387. distance = 0;
  388. } else {
  389. distance = (offset - this.options.boundary) * this.multiplier;
  390. if (distance > this.listLength - this.containerLength) {
  391. distance = this.listLength - this.containerLength;
  392. }
  393. }
  394. this.updatePosition(-distance);
  395. }
  396. }
  397. isMatchScroll(event) {
  398. if (!this.is('disabled') && this.canScroll()) {
  399. if (this.options.exception) {
  400. if ($$1(event.target).closest(this.options.exception).length === 0) {
  401. return true;
  402. }
  403. return false;
  404. }
  405. return true;
  406. }
  407. return false;
  408. }
  409. canScroll() {
  410. return this.listLength > this.containerLength;
  411. }
  412. getContainerLength() {
  413. return this.element[this.attributes.clientLength];
  414. }
  415. getListhLength() {
  416. return this.$list[0][this.attributes.clientLength];
  417. }
  418. updateLength() {
  419. this.containerLength = this.getContainerLength();
  420. this.listLength = this.getListhLength();
  421. this.multiplier = (this.listLength - this.containerLength) / (this.containerLength - 2 * this.options.boundary);
  422. }
  423. initPosition() {
  424. const style = this.makePositionStyle(0);
  425. this.$list.css(style);
  426. }
  427. getPosition() {
  428. let value;
  429. if (this.options.useCssTransforms && support.transform) {
  430. if (this.options.useCssTransforms3d && support.transform3d) {
  431. value = support.convertMatrixToArray(this.$list.css(support.transform));
  432. } else {
  433. value = support.convertMatrixToArray(this.$list.css(support.transform));
  434. }
  435. if (!value) {
  436. return 0;
  437. }
  438. if (this.attributes.axis === 'X') {
  439. value = value[12] || value[4];
  440. } else {
  441. value = value[13] || value[5];
  442. }
  443. } else {
  444. value = this.$list.css(this.attributes.position);
  445. }
  446. return parseFloat(value.replace('px', ''));
  447. }
  448. makePositionStyle(value) {
  449. let property;
  450. let x = '0px';
  451. let y = '0px';
  452. if (this.options.useCssTransforms && support.transform) {
  453. if (this.attributes.axis === 'X') {
  454. x = `${value}px`;
  455. } else {
  456. y = `${value}px`;
  457. }
  458. property = support.transform.toString();
  459. if (this.options.useCssTransforms3d && support.transform3d) {
  460. value = `translate3d(${x},${y},0px)`;
  461. } else {
  462. value = `translate(${x},${y})`;
  463. }
  464. } else {
  465. property = this.attributes.position;
  466. }
  467. const temp = {};
  468. temp[property] = value;
  469. return temp;
  470. }
  471. updatePosition(value) {
  472. const style = this.makePositionStyle(value);
  473. this.$list.css(style);
  474. }
  475. update() {
  476. if (!this.is('disabled')) {
  477. this.updateLength();
  478. if (!this.canScroll()) {
  479. this.initPosition();
  480. }
  481. }
  482. }
  483. eventName(events) {
  484. if (typeof events !== 'string' || events === '') {
  485. return `.${NAMESPACE$1}`;
  486. }
  487. events = events.split(' ');
  488. const length = events.length;
  489. for (let i = 0; i < length; i++) {
  490. events[i] = `${events[i]}.${NAMESPACE$1}`;
  491. }
  492. return events.join(' ');
  493. }
  494. eventNameWithId(events) {
  495. if (typeof events !== 'string' || events === '') {
  496. return `.${this.options.namespace}-${this.instanceId}`;
  497. }
  498. events = events.split(' ');
  499. const length = events.length;
  500. for (let i = 0; i < length; i++) {
  501. events[i] = `${events[i]}.${this.options.namespace}-${this.instanceId}`;
  502. }
  503. return events.join(' ');
  504. }
  505. trigger(eventType, ...params) {
  506. const data = [this].concat(params);
  507. // event
  508. this.$element.trigger(`${NAMESPACE$1}::${eventType}`, data);
  509. // callback
  510. eventType = eventType.replace(/\b\w+\b/g, (word) => {
  511. return word.substring(0, 1).toUpperCase() + word.substring(1);
  512. });
  513. const onFunction = `on${eventType}`;
  514. if (typeof this.options[onFunction] === 'function') {
  515. this.options[onFunction].apply(this, params);
  516. }
  517. }
  518. /**
  519. * Checks whether the carousel is in a specific state or not.
  520. */
  521. is(state) {
  522. return this._states[state] && this._states[state] > 0;
  523. }
  524. /**
  525. * Enters a state.
  526. */
  527. enter(state) {
  528. if (this._states[state] === undefined) {
  529. this._states[state] = 0;
  530. }
  531. this._states[state] = 1;
  532. }
  533. /**
  534. * Leaves a state.
  535. */
  536. leave(state) {
  537. this._states[state] = 0;
  538. }
  539. /**
  540. * _throttle
  541. * @description Borrowed from Underscore.js
  542. */
  543. throttle(func, wait) {
  544. const _now = Date.now || function() {
  545. return new Date().getTime();
  546. };
  547. let timeout;
  548. let context;
  549. let args;
  550. let result;
  551. let previous = 0;
  552. let later = function() {
  553. previous = _now();
  554. timeout = null;
  555. result = func.apply(context, args);
  556. if (!timeout) {
  557. context = args = null;
  558. }
  559. };
  560. return (...params) => {
  561. /*eslint consistent-this: "off"*/
  562. let now = _now();
  563. let remaining = wait - (now - previous);
  564. context = this;
  565. args = params;
  566. if (remaining <= 0 || remaining > wait) {
  567. if (timeout) {
  568. clearTimeout(timeout);
  569. timeout = null;
  570. }
  571. previous = now;
  572. result = func.apply(context, args);
  573. if (!timeout) {
  574. context = args = null;
  575. }
  576. } else if (!timeout) {
  577. timeout = setTimeout(later, remaining);
  578. }
  579. return result;
  580. };
  581. }
  582. enable() {
  583. if (this.is('disabled')) {
  584. this.leave('disabled');
  585. this.$element.removeClass(this.classes.disabled);
  586. this.bindEvents();
  587. }
  588. this.trigger('enable');
  589. }
  590. disable() {
  591. if (!this.is('disabled')) {
  592. this.enter('disabled');
  593. this.initPosition();
  594. this.$element.addClass(this.classes.disabled);
  595. this.unbindEvents();
  596. }
  597. this.trigger('disable');
  598. }
  599. destroy() {
  600. this.$element.removeClass(this.classes.disabled);
  601. this.unbindEvents();
  602. this.$element.data(NAMESPACE$1, null);
  603. this.trigger('destroy');
  604. }
  605. static setDefaults(options) {
  606. $$1.extend(DEFAULTS, $$1.isPlainObject(options) && options);
  607. }
  608. }
  609. var info = {
  610. version:'0.3.7'
  611. };
  612. const NAMESPACE = 'asHoverScroll';
  613. const OtherAsHoverScroll = $$1.fn.asHoverScroll;
  614. const jQueryAsHoverScroll = function(options, ...args) {
  615. if (typeof options === 'string') {
  616. const method = options;
  617. if (/^_/.test(method)) {
  618. return false;
  619. } else if ((/^(get)/.test(method))) {
  620. const instance = this.first().data(NAMESPACE);
  621. if (instance && typeof instance[method] === 'function') {
  622. return instance[method](...args);
  623. }
  624. } else {
  625. return this.each(function() {
  626. const instance = $$1.data(this, NAMESPACE);
  627. if (instance && typeof instance[method] === 'function') {
  628. instance[method](...args);
  629. }
  630. });
  631. }
  632. }
  633. return this.each(function() {
  634. if (!$$1(this).data(NAMESPACE)) {
  635. $$1(this).data(NAMESPACE, new asHoverScroll(this, options));
  636. }
  637. });
  638. };
  639. $$1.fn.asHoverScroll = jQueryAsHoverScroll;
  640. $$1.asHoverScroll = $$1.extend({
  641. setDefaults: asHoverScroll.setDefaults,
  642. noConflict: function() {
  643. $$1.fn.asHoverScroll = OtherAsHoverScroll;
  644. return jQueryAsHoverScroll;
  645. }
  646. }, info);