UNPKG

react-lory

Version:

[Deprecated] Use react-slidy instead.

417 lines (338 loc) 11.5 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.slidy = slidy; var _detectPrefixes = require('./detect-prefixes.js'); var _detectPrefixes2 = _interopRequireDefault(_detectPrefixes); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var slice = Array.prototype.slice; var prefixes = (0, _detectPrefixes2.default)(); var LINEAR_ANIMATION = 'linear'; var SLIDES_TO_SCROLL = 1; var VALID_SWIPE_DISTANCE = 25; function slidy(slider, options) { var ease = options.ease; var infinite = options.infinite; var items = options.items; var rewind = options.rewind; var rewindSpeed = options.rewindSpeed; var slideSpeed = options.slideSpeed; var abs = Math.abs; var floor = Math.floor; var min = Math.min; var max = Math.max; var round = Math.round; var frameDOMEl = options.frameDOMEl; var windowDOM = window; // DOM elements var slideContainerDOMEl = frameDOMEl.getElementsByClassName(options.classNameSlideContainer)[0]; // initialize some variables var frameWidth = 0; var index = 0; var loadedIndex = { 0: 1, 1: 1 }; var maxOffset = 0; var position = 0; var slides = []; var transitionEndCallback = void 0; // event handling var touchOffset = { pageX: 0, pageY: 0 }; var currentTouchOffset = { pageX: 0, pageY: 0 }; var delta = { x: 0, y: 0 }; var isScrolling = false; // clamp a number between two min and max values function _clampNumber(x, minValue, maxValue) { return min(max(x, minValue), maxValue); } // get the width from a DOM element function _getWidthFromDOMEl(el) { return el.getBoundingClientRect().width; } // calculate the offset with the width of the frame and the desired position function _getOffsetLeft(slidePosition) { return frameWidth * slidePosition; } /** * private * _setupInfinite: function to setup if infinite is set * * @param {array} slideArray * @return {array} array of updated slideContainer elements */ function _setupInfinite(slideArray) { var infinite = options.infinite; var totalSlides = slideArray.length; var front = slideArray.slice(0, infinite); var back = slideArray.slice(totalSlides - infinite, totalSlides); var firstChild = slideContainerDOMEl.firstChild; front.forEach(function (el) { var cloned = el.cloneNode(true); slideContainerDOMEl.appendChild(cloned); }); back.reverse().forEach(function (el) { var cloned = el.cloneNode(true); slideContainerDOMEl.insertBefore(cloned, firstChild); }); return slice.call(slideContainerDOMEl.children); } /** * translates to a given position in a given time in milliseconds * * @to {number} number in pixels where to translate to * @duration {number} time in milliseconds for the transistion * @ease {string} easing css property */ function _translate(to, duration, ease) { var easeCssText = ease ? prefixes.transitionTiming + ': ' + ease + ';' : ''; var durationCssText = duration ? prefixes.transitionDuration + ': ' + duration + 'ms;' : ''; var cssText = '' + easeCssText + durationCssText + '\n ' + prefixes.transform + ': ' + prefixes.translate(to) + ';'; slideContainerDOMEl.style.cssText = cssText; } /** * slidefunction called by prev, next & touchend * * determine nextIndex and slide to next postion * under restrictions of the defined options * * @direction {boolean} */ function slide(direction) { var duration = slideSpeed; var movement = direction ? 1 : -1; var totalSlides = slides.length; // calculate the nextIndex according to the movement var nextIndex = index + SLIDES_TO_SCROLL * movement; // nextIndex should be between 0 and totalSlides minus 1 nextIndex = _clampNumber(nextIndex, 0, totalSlides - 1); if (infinite && direction === undefined) { nextIndex += infinite; } var nextOffset = _clampNumber(_getOffsetLeft(nextIndex) * -1, maxOffset * -1, 0); if (rewind && direction && abs(position) === maxOffset) { nextOffset = 0; nextIndex = 0; duration = rewindSpeed; } // translate to the nextOffset by a defined duration and ease function _translate(nextOffset, duration, ease); // update the position with the next position position = nextOffset; // if the nextIndex is possible according to totalSlides, then use it if (nextIndex <= totalSlides) { index = nextIndex; } if (infinite && (nextIndex === totalSlides - infinite || nextIndex === 0)) { index = direction ? infinite : totalSlides - infinite * 2; position = _getOffsetLeft(index) * -1; transitionEndCallback = function transitionEndCallback() { _translate(_getOffsetLeft(index) * -1, 0); }; } else { var indexToLoad = index + SLIDES_TO_SCROLL * movement; var indexLoaded = !!loadedIndex[indexToLoad]; if (indexToLoad < totalSlides && indexToLoad >= 0 && !indexLoaded) { // insert in the correct position slideContainerDOMEl.appendChild(slides[indexToLoad]); loadedIndex[indexToLoad] = 1; } } options.doAfterSlide({ currentSlide: index }); } function _removeTouchEventsListeners() { var all = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; frameDOMEl.removeEventListener('touchmove', onTouchmove); frameDOMEl.removeEventListener('touchend', onTouchend); frameDOMEl.removeEventListener('touchcancel', onTouchend); if (all) { frameDOMEl.removeEventListener('touchstart', onTouchstart); } } function _removeAllEventsListeners() { _removeTouchEventsListeners(true); frameDOMEl.removeEventListener(prefixes.transitionEnd, onTransitionEnd); windowDOM.removeEventListener('resize', onResize); } function onTransitionEnd() { if (transitionEndCallback) { transitionEndCallback(); transitionEndCallback = undefined; } } function onTouchstart(event) { var _ref = event.targetTouches ? event.targetTouches[0] : event; var pageX = _ref.pageX; var pageY = _ref.pageY; touchOffset = currentTouchOffset = { pageX: pageX, pageY: pageY }; frameDOMEl.addEventListener('touchmove', onTouchmove); frameDOMEl.addEventListener('touchend', onTouchend); frameDOMEl.addEventListener('touchcancel', onTouchend); } function onTouchmove(event) { var _ref2 = event.targetTouches ? event.targetTouches[0] : event; var pageX = _ref2.pageX; var pageY = _ref2.pageY; delta = { x: pageX - touchOffset.pageX, y: pageY - touchOffset.pageY }; var deltaNow = { x: pageX - currentTouchOffset.pageX, y: pageY - currentTouchOffset.pageY }; currentTouchOffset = { pageX: pageX, pageY: pageY }; var isScrollingNow = abs(deltaNow.x) < abs(deltaNow.y); isScrolling = !!(isScrolling || isScrollingNow); if (navigator.userAgent.indexOf('Android 4.3') >= 0 && !isScrollingNow) { event.preventDefault(); } if (!isScrolling && delta.x !== 0) { _translate(Math.round(position + delta.x), 0); } else if (isScrolling) { onTouchend(event); } } function onTouchend(event) { /** * is valid if: * -> swipe distance is greater than the specified valid swipe distance * -> swipe distance is more then a third of the swipe area * @isValidSlide {Boolean} */ var absoluteX = abs(delta.x); var isValid = absoluteX > VALID_SWIPE_DISTANCE || absoluteX > frameWidth / 3; /** * is out of bounds if: * -> index is 0 and delta x is greater than 0 * -> index is the last slide and delta is smaller than 0 * @isOutOfBounds {Boolean} */ var direction = delta.x < 0; var isOutOfBounds = !index && !direction || index === slides.length - 1 && direction; if (isValid && !isOutOfBounds) { slide(direction); } else { _translate(position, options.snapBackSpeed, LINEAR_ANIMATION); } delta = {}; touchOffset = {}; isScrolling = false; _removeTouchEventsListeners(); } function _convertItemToDOM(string) { var wrappedString = '<li class=\'' + options.classNameItem + '\'>' + string + '</li>'; var el = document.createElement('template'); if (el.content) { el.innerHTML = wrappedString; return el.content; } else { var container = document.createElement('ul'); var fragment = document.createDocumentFragment(); container.innerHTML = string; var root = container; while (root.firstChild) { fragment.appendChild(root.firstChild); } return fragment; } } function onResize(event) { reset(); } /** * public * setup function */ function _setup() { var infinite = options.infinite; var slidesArray = slice.call(items.map(function (i) { return _convertItemToDOM(i); })); position = slideContainerDOMEl.offsetLeft; slides = infinite ? _setupInfinite(slidesArray) : slice.call(slidesArray); reset(); slideContainerDOMEl.addEventListener(prefixes.transitionEnd, onTransitionEnd); frameDOMEl.addEventListener('touchstart', onTouchstart); windowDOM.addEventListener('resize', onResize); } /** * public * reset function: called on resize */ function reset() { var infinite = options.infinite; var rewindOnResize = options.rewindOnResize; var ease = options.ease; var rewindSpeed = options.rewindSpeed; frameWidth = _getWidthFromDOMEl(frameDOMEl); maxOffset = round(frameWidth * slides.length - frameWidth); var slidesHeight = floor(slideContainerDOMEl.firstChild.getBoundingClientRect().height) + 'px'; slideContainerDOMEl.style.height = slidesHeight; frameDOMEl.style.height = slidesHeight; if (rewindOnResize) { index = 0; } else { ease = null; rewindSpeed = 0; } var offsetIndex = infinite ? index + infinite : index; var newX = _getOffsetLeft(offsetIndex) * -1; if (infinite) { _translate(newX, 0); } else { _translate(newX, rewindSpeed, ease); } index = offsetIndex; position = newX; } /** * public * returnIndex function: called on clickhandler */ function returnIndex() { return index - options.infinite || 0; } /** * public * prev function: called on clickhandler */ function prev() { slide(false); } /** * public * next function: called on clickhandler */ function next() { slide(true); } /** * public * destroy function: called to gracefully destroy the slidy instance */ function destroy() { var infinite = options.infinite; _removeAllEventsListeners(); // remove cloned slides if infinite is set if (infinite) { (function () { var firstChild = slideContainerDOMEl.firstChild; var lastChild = slideContainerDOMEl.lastChild; Array.apply(null, Array(infinite)).forEach(function () { slideContainerDOMEl.removeChild(firstChild); slideContainerDOMEl.removeChild(lastChild); }); })(); } } // trigger initial setup _setup(); // expose public api return { reset: reset, slide: slide, returnIndex: returnIndex, prev: prev, next: next, destroy: destroy }; }