react-lory
Version:
[Deprecated] Use react-slidy instead.
417 lines (338 loc) • 11.5 kB
JavaScript
'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
};
}