UNPKG

swiper

Version:

Most modern mobile touch slider and framework with hardware accelerated transitions

483 lines (399 loc) 17 kB
"use strict"; exports.__esModule = true; exports.default = void 0; var _ssrWindow = require("ssr-window"); var _dom = _interopRequireDefault(require("../../utils/dom")); var _utils = require("../../utils/utils"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function isEventSupported() { var document = (0, _ssrWindow.getDocument)(); var eventName = 'onwheel'; var isSupported = (eventName in document); if (!isSupported) { var element = document.createElement('div'); element.setAttribute(eventName, 'return;'); isSupported = typeof element[eventName] === 'function'; } if (!isSupported && document.implementation && document.implementation.hasFeature && // always returns true in newer browsers as per the standard. // @see http://dom.spec.whatwg.org/#dom-domimplementation-hasfeature document.implementation.hasFeature('', '') !== true) { // This is the only way to test support for the `wheel` event in IE9+. isSupported = document.implementation.hasFeature('Events.wheel', '3.0'); } return isSupported; } var Mousewheel = { lastScrollTime: (0, _utils.now)(), lastEventBeforeSnap: undefined, recentWheelEvents: [], event: function event() { var window = (0, _ssrWindow.getWindow)(); if (window.navigator.userAgent.indexOf('firefox') > -1) return 'DOMMouseScroll'; return isEventSupported() ? 'wheel' : 'mousewheel'; }, normalize: function normalize(e) { // Reasonable defaults var PIXEL_STEP = 10; var LINE_HEIGHT = 40; var PAGE_HEIGHT = 800; var sX = 0; var sY = 0; // spinX, spinY var pX = 0; var pY = 0; // pixelX, pixelY // Legacy if ('detail' in e) { sY = e.detail; } if ('wheelDelta' in e) { sY = -e.wheelDelta / 120; } if ('wheelDeltaY' in e) { sY = -e.wheelDeltaY / 120; } if ('wheelDeltaX' in e) { sX = -e.wheelDeltaX / 120; } // side scrolling on FF with DOMMouseScroll if ('axis' in e && e.axis === e.HORIZONTAL_AXIS) { sX = sY; sY = 0; } pX = sX * PIXEL_STEP; pY = sY * PIXEL_STEP; if ('deltaY' in e) { pY = e.deltaY; } if ('deltaX' in e) { pX = e.deltaX; } if (e.shiftKey && !pX) { // if user scrolls with shift he wants horizontal scroll pX = pY; pY = 0; } if ((pX || pY) && e.deltaMode) { if (e.deltaMode === 1) { // delta in LINE units pX *= LINE_HEIGHT; pY *= LINE_HEIGHT; } else { // delta in PAGE units pX *= PAGE_HEIGHT; pY *= PAGE_HEIGHT; } } // Fall-back if spin cannot be determined if (pX && !sX) { sX = pX < 1 ? -1 : 1; } if (pY && !sY) { sY = pY < 1 ? -1 : 1; } return { spinX: sX, spinY: sY, pixelX: pX, pixelY: pY }; }, handleMouseEnter: function handleMouseEnter() { var swiper = this; swiper.mouseEntered = true; }, handleMouseLeave: function handleMouseLeave() { var swiper = this; swiper.mouseEntered = false; }, handle: function handle(event) { var e = event; var disableParentSwiper = true; var swiper = this; var params = swiper.params.mousewheel; if (swiper.params.cssMode) { e.preventDefault(); } var target = swiper.$el; if (swiper.params.mousewheel.eventsTarget !== 'container') { target = (0, _dom.default)(swiper.params.mousewheel.eventsTarget); } if (!swiper.mouseEntered && !target[0].contains(e.target) && !params.releaseOnEdges) return true; if (e.originalEvent) e = e.originalEvent; // jquery fix var delta = 0; var rtlFactor = swiper.rtlTranslate ? -1 : 1; var data = Mousewheel.normalize(e); if (params.forceToAxis) { if (swiper.isHorizontal()) { if (Math.abs(data.pixelX) > Math.abs(data.pixelY)) delta = -data.pixelX * rtlFactor;else return true; } else if (Math.abs(data.pixelY) > Math.abs(data.pixelX)) delta = -data.pixelY;else return true; } else { delta = Math.abs(data.pixelX) > Math.abs(data.pixelY) ? -data.pixelX * rtlFactor : -data.pixelY; } if (delta === 0) return true; if (params.invert) delta = -delta; // Get the scroll positions var positions = swiper.getTranslate() + delta * params.sensitivity; if (positions >= swiper.minTranslate()) positions = swiper.minTranslate(); if (positions <= swiper.maxTranslate()) positions = swiper.maxTranslate(); // When loop is true: // the disableParentSwiper will be true. // When loop is false: // if the scroll positions is not on edge, // then the disableParentSwiper will be true. // if the scroll on edge positions, // then the disableParentSwiper will be false. disableParentSwiper = swiper.params.loop ? true : !(positions === swiper.minTranslate() || positions === swiper.maxTranslate()); if (disableParentSwiper && swiper.params.nested) e.stopPropagation(); if (!swiper.params.freeMode) { // Register the new event in a variable which stores the relevant data var newEvent = { time: (0, _utils.now)(), delta: Math.abs(delta), direction: Math.sign(delta), raw: event }; // Keep the most recent events var recentWheelEvents = swiper.mousewheel.recentWheelEvents; if (recentWheelEvents.length >= 2) { recentWheelEvents.shift(); // only store the last N events } var prevEvent = recentWheelEvents.length ? recentWheelEvents[recentWheelEvents.length - 1] : undefined; recentWheelEvents.push(newEvent); // If there is at least one previous recorded event: // If direction has changed or // if the scroll is quicker than the previous one: // Animate the slider. // Else (this is the first time the wheel is moved): // Animate the slider. if (prevEvent) { if (newEvent.direction !== prevEvent.direction || newEvent.delta > prevEvent.delta || newEvent.time > prevEvent.time + 150) { swiper.mousewheel.animateSlider(newEvent); } } else { swiper.mousewheel.animateSlider(newEvent); } // If it's time to release the scroll: // Return now so you don't hit the preventDefault. if (swiper.mousewheel.releaseScroll(newEvent)) { return true; } } else { // Freemode or scrollContainer: // If we recently snapped after a momentum scroll, then ignore wheel events // to give time for the deceleration to finish. Stop ignoring after 500 msecs // or if it's a new scroll (larger delta or inverse sign as last event before // an end-of-momentum snap). var _newEvent = { time: (0, _utils.now)(), delta: Math.abs(delta), direction: Math.sign(delta) }; var lastEventBeforeSnap = swiper.mousewheel.lastEventBeforeSnap; var ignoreWheelEvents = lastEventBeforeSnap && _newEvent.time < lastEventBeforeSnap.time + 500 && _newEvent.delta <= lastEventBeforeSnap.delta && _newEvent.direction === lastEventBeforeSnap.direction; if (!ignoreWheelEvents) { swiper.mousewheel.lastEventBeforeSnap = undefined; if (swiper.params.loop) { swiper.loopFix(); } var position = swiper.getTranslate() + delta * params.sensitivity; var wasBeginning = swiper.isBeginning; var wasEnd = swiper.isEnd; if (position >= swiper.minTranslate()) position = swiper.minTranslate(); if (position <= swiper.maxTranslate()) position = swiper.maxTranslate(); swiper.setTransition(0); swiper.setTranslate(position); swiper.updateProgress(); swiper.updateActiveIndex(); swiper.updateSlidesClasses(); if (!wasBeginning && swiper.isBeginning || !wasEnd && swiper.isEnd) { swiper.updateSlidesClasses(); } if (swiper.params.freeModeSticky) { // When wheel scrolling starts with sticky (aka snap) enabled, then detect // the end of a momentum scroll by storing recent (N=15?) wheel events. // 1. do all N events have decreasing or same (absolute value) delta? // 2. did all N events arrive in the last M (M=500?) msecs? // 3. does the earliest event have an (absolute value) delta that's // at least P (P=1?) larger than the most recent event's delta? // 4. does the latest event have a delta that's smaller than Q (Q=6?) pixels? // If 1-4 are "yes" then we're near the end of a momentum scroll deceleration. // Snap immediately and ignore remaining wheel events in this scroll. // See comment above for "remaining wheel events in this scroll" determination. // If 1-4 aren't satisfied, then wait to snap until 500ms after the last event. clearTimeout(swiper.mousewheel.timeout); swiper.mousewheel.timeout = undefined; var _recentWheelEvents = swiper.mousewheel.recentWheelEvents; if (_recentWheelEvents.length >= 15) { _recentWheelEvents.shift(); // only store the last N events } var _prevEvent = _recentWheelEvents.length ? _recentWheelEvents[_recentWheelEvents.length - 1] : undefined; var firstEvent = _recentWheelEvents[0]; _recentWheelEvents.push(_newEvent); if (_prevEvent && (_newEvent.delta > _prevEvent.delta || _newEvent.direction !== _prevEvent.direction)) { // Increasing or reverse-sign delta means the user started scrolling again. Clear the wheel event log. _recentWheelEvents.splice(0); } else if (_recentWheelEvents.length >= 15 && _newEvent.time - firstEvent.time < 500 && firstEvent.delta - _newEvent.delta >= 1 && _newEvent.delta <= 6) { // We're at the end of the deceleration of a momentum scroll, so there's no need // to wait for more events. Snap ASAP on the next tick. // Also, because there's some remaining momentum we'll bias the snap in the // direction of the ongoing scroll because it's better UX for the scroll to snap // in the same direction as the scroll instead of reversing to snap. Therefore, // if it's already scrolled more than 20% in the current direction, keep going. var snapToThreshold = delta > 0 ? 0.8 : 0.2; swiper.mousewheel.lastEventBeforeSnap = _newEvent; _recentWheelEvents.splice(0); swiper.mousewheel.timeout = (0, _utils.nextTick)(function () { swiper.slideToClosest(swiper.params.speed, true, undefined, snapToThreshold); }, 0); // no delay; move on next tick } if (!swiper.mousewheel.timeout) { // if we get here, then we haven't detected the end of a momentum scroll, so // we'll consider a scroll "complete" when there haven't been any wheel events // for 500ms. swiper.mousewheel.timeout = (0, _utils.nextTick)(function () { var snapToThreshold = 0.5; swiper.mousewheel.lastEventBeforeSnap = _newEvent; _recentWheelEvents.splice(0); swiper.slideToClosest(swiper.params.speed, true, undefined, snapToThreshold); }, 500); } } // Emit event if (!ignoreWheelEvents) swiper.emit('scroll', e); // Stop autoplay if (swiper.params.autoplay && swiper.params.autoplayDisableOnInteraction) swiper.autoplay.stop(); // Return page scroll on edge positions if (position === swiper.minTranslate() || position === swiper.maxTranslate()) return true; } } if (e.preventDefault) e.preventDefault();else e.returnValue = false; return false; }, animateSlider: function animateSlider(newEvent) { var swiper = this; var window = (0, _ssrWindow.getWindow)(); if (this.params.mousewheel.thresholdDelta && newEvent.delta < this.params.mousewheel.thresholdDelta) { // Prevent if delta of wheel scroll delta is below configured threshold return false; } if (this.params.mousewheel.thresholdTime && (0, _utils.now)() - swiper.mousewheel.lastScrollTime < this.params.mousewheel.thresholdTime) { // Prevent if time between scrolls is below configured threshold return false; } // If the movement is NOT big enough and // if the last time the user scrolled was too close to the current one (avoid continuously triggering the slider): // Don't go any further (avoid insignificant scroll movement). if (newEvent.delta >= 6 && (0, _utils.now)() - swiper.mousewheel.lastScrollTime < 60) { // Return false as a default return true; } // If user is scrolling towards the end: // If the slider hasn't hit the latest slide or // if the slider is a loop and // if the slider isn't moving right now: // Go to next slide and // emit a scroll event. // Else (the user is scrolling towards the beginning) and // if the slider hasn't hit the first slide or // if the slider is a loop and // if the slider isn't moving right now: // Go to prev slide and // emit a scroll event. if (newEvent.direction < 0) { if ((!swiper.isEnd || swiper.params.loop) && !swiper.animating) { swiper.slideNext(); swiper.emit('scroll', newEvent.raw); } } else if ((!swiper.isBeginning || swiper.params.loop) && !swiper.animating) { swiper.slidePrev(); swiper.emit('scroll', newEvent.raw); } // If you got here is because an animation has been triggered so store the current time swiper.mousewheel.lastScrollTime = new window.Date().getTime(); // Return false as a default return false; }, releaseScroll: function releaseScroll(newEvent) { var swiper = this; var params = swiper.params.mousewheel; if (newEvent.direction < 0) { if (swiper.isEnd && !swiper.params.loop && params.releaseOnEdges) { // Return true to animate scroll on edges return true; } } else if (swiper.isBeginning && !swiper.params.loop && params.releaseOnEdges) { // Return true to animate scroll on edges return true; } return false; }, enable: function enable() { var swiper = this; var event = Mousewheel.event(); if (swiper.params.cssMode) { swiper.wrapperEl.removeEventListener(event, swiper.mousewheel.handle); return true; } if (!event) return false; if (swiper.mousewheel.enabled) return false; var target = swiper.$el; if (swiper.params.mousewheel.eventsTarget !== 'container') { target = (0, _dom.default)(swiper.params.mousewheel.eventsTarget); } target.on('mouseenter', swiper.mousewheel.handleMouseEnter); target.on('mouseleave', swiper.mousewheel.handleMouseLeave); target.on(event, swiper.mousewheel.handle); swiper.mousewheel.enabled = true; return true; }, disable: function disable() { var swiper = this; var event = Mousewheel.event(); if (swiper.params.cssMode) { swiper.wrapperEl.addEventListener(event, swiper.mousewheel.handle); return true; } if (!event) return false; if (!swiper.mousewheel.enabled) return false; var target = swiper.$el; if (swiper.params.mousewheel.eventsTarget !== 'container') { target = (0, _dom.default)(swiper.params.mousewheel.eventsTarget); } target.off(event, swiper.mousewheel.handle); swiper.mousewheel.enabled = false; return true; } }; var _default = { name: 'mousewheel', params: { mousewheel: { enabled: false, releaseOnEdges: false, invert: false, forceToAxis: false, sensitivity: 1, eventsTarget: 'container', thresholdDelta: null, thresholdTime: null } }, create: function create() { var swiper = this; (0, _utils.bindModuleMethods)(swiper, { mousewheel: { enabled: false, lastScrollTime: (0, _utils.now)(), lastEventBeforeSnap: undefined, recentWheelEvents: [], enable: Mousewheel.enable, disable: Mousewheel.disable, handle: Mousewheel.handle, handleMouseEnter: Mousewheel.handleMouseEnter, handleMouseLeave: Mousewheel.handleMouseLeave, animateSlider: Mousewheel.animateSlider, releaseScroll: Mousewheel.releaseScroll } }); }, on: { init: function init(swiper) { if (!swiper.params.mousewheel.enabled && swiper.params.cssMode) { swiper.mousewheel.disable(); } if (swiper.params.mousewheel.enabled) swiper.mousewheel.enable(); }, destroy: function destroy(swiper) { if (swiper.params.cssMode) { swiper.mousewheel.enable(); } if (swiper.mousewheel.enabled) swiper.mousewheel.disable(); } } }; exports.default = _default;