/* eslint-disable */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define([], factory) : (global.MomentumSlider = factory()); }(this, (function () { 'use strict'; /* eslint-enable */ // fixes weird safari 10 bug where preventDefault is prevented // @see https://github.com/metafizzy/flickity/issues/457#issuecomment-254501356 window.addEventListener('touchmove', function () {}); function MomentumSlider(options) { this.o = extend({}, this.defaults, options); this.initHtml(); this.initValues(); this.initEvents(); this.updateClassnames(); } MomentumSlider.prototype = { defaults: { el: '.ms-container', cssClass: '', vertical: false, multiplier: 1, bounceCoefficient: 0.3, bounceMax: 100, loop: 0, interactive: true, reverse: false, currentIndex: 0 }, initHtml: function () { this.msContainer = is.str(this.o.el) ? document.querySelector(this.o.el) : this.o.el; if (this.o.range) { var html = '
'; var msHtml = document.createElement('div'); msHtml.innerHTML = html; this.msContainer.appendChild(msHtml.firstChild); this.msContainer = this.msContainer.lastChild; } this.msContainer.classList.add('ms-container--' + (this.o.vertical ? 'vertical' : 'horizontal')); if (this.o.reverse) { this.msContainer.classList.add('ms-container--reverse'); } this.msTrack = this.msContainer.children[0]; this.msSlides = this.msTrack.children; this.step = this.o.vertical ? this.msSlides[0].scrollHeight : this.msSlides[0].scrollWidth; this.sliderLength = this.msSlides.length; if (this.o.loop) { var loopLength, slideIndex, fragment; // begin fragment = document.createDocumentFragment(); loopLength = this.o.loop; slideIndex = this.sliderLength - loopLength; while (loopLength--) { fragment.appendChild(this.msSlides[slideIndex++].cloneNode(true)); } this.msTrack.insertBefore(fragment, this.msTrack.firstChild); // end fragment = document.createDocumentFragment(); slideIndex = loopLength = this.o.loop; while (loopLength--) { fragment.appendChild(this.msSlides[slideIndex++].cloneNode(true)); } this.msTrack.appendChild(fragment); // update this.sliderLength += this.o.loop * 2; } this.sliderWidth = this.sliderLength * this.step; }, initValues: function () { this.boundMin = this.o.reverse ? 0 : -this.step * (this.sliderLength - 1); this.boundMax = this.o.reverse ? this.step * (this.sliderLength - 1) : 0; this.targetPosition = this.targetPosition || 0; this.ticking = false; this.enabled = true; this.pointerActive = false; this.pointerMoved = false; this.trackingPoints = []; this.msTrack.style[this.o.vertical ? 'height' : 'width'] = this.sliderWidth + 'px'; this.currentIndex = (this.currentIndex || this.o.currentIndex) + this.o.loop; this.updateSlider(undefined, true); this.renderTarget(); var index = this.sliderLength; while (index--) { this.setStyle(index, this.currentIndex == index ? 0 : -1); } }, initEvents: function () { if (this.o.interactive) { this.msContainer.addEventListener('touchstart', this.onDown.bind(this)); this.msContainer.addEventListener('mousedown', this.onDown.bind(this)); document.addEventListener('touchmove', this.onMove.bind(this), getPassiveSupported() ? { passive: false } : false); document.addEventListener('touchend', this.onUp.bind(this)); document.addEventListener('touchcancel', this.stopTracking.bind(this)); document.addEventListener('mousemove', this.onMove.bind(this), getPassiveSupported() ? { passive: false } : false); document.addEventListener('mouseup', this.onUp.bind(this)); if (this.o.prevEl) { this.prevEl = is.str(this.o.prevEl) ? document.querySelector(this.o.prevEl) : this.o.prevEl; this.prevEl.addEventListener('click', this.prev.bind(this)); } if (this.o.nextEl) { this.nextEl = is.str(this.o.nextEl) ? document.querySelector(this.o.nextEl) : this.o.nextEl; this.nextEl.addEventListener('click', this.next.bind(this)); } } window.addEventListener('resize', this.onResize.bind(this)); }, prev: function () { if (this.enabled) { this.updateSlider(Math.round(this.targetPosition / this.step) * this.step + (this.o.reverse ? -this.step : this.step)); } }, next: function () { if (this.enabled) { this.updateSlider(Math.round(this.targetPosition / this.step) * this.step + (this.o.reverse ? this.step : -this.step)); } }, select: function (index) { if (this.enabled) { // this.currentIndex = index; // this.updateSlider(); this.updateSlider((index + this.o.loop) * (this.o.reverse ? this.step : -this.step)); } }, setStyleToNode: function (node, style, diff, lower) { if (style) { var value = ''; for (var property in style) { if (property[0] == '.') { this.setStyleToNode(node.querySelector(property), style[property], diff, lower); } else if (property == 'transform') { style[property].forEach(function(transform) { for (var t in transform) { value += t + '(' + getCurrentValue(transform[t], diff, lower); if (t == 'rotate') { value += 'deg'; } else if (t == 'translateX' || t == 'translateY' || t == 'translateZ') { value += 'px'; } value += ') '; } }); } else { value = getCurrentValue(style[property], diff, lower); } node.style[property] = value; } } }, setStyle: function (index, diff, lower) { this.setStyleToNode(this.msSlides[index], this.o.style, diff, lower); if (is.fnc(this.o.customStyles)) { this.o.customStyles(index, diff, lower); } }, renderTarget: function () { if (this.o.sync) { var syncIndex = this.o.sync.length; var syncSlider; while (syncIndex--) { syncSlider = this.o.sync[syncIndex]; syncSlider.targetPosition = (syncSlider.o.reverse ? -1 : 1) * this.targetPosition / this.sliderWidth * syncSlider.sliderWidth; syncSlider.renderTarget(); } } var paddingLength = this.o.loop * this.step; var contentLength = this.sliderWidth - (paddingLength * 2); if (this.o.loop) { if (-this.targetPosition < paddingLength) { while (-this.targetPosition < paddingLength) { this.targetPosition -= contentLength; } } else if (-this.targetPosition >= paddingLength + contentLength) { while (-this.targetPosition >= paddingLength + contentLength) { this.targetPosition += contentLength; } } } // var actualIndex = -this.targetPosition / this.step; var actualIndex = (this.o.reverse ? 1 : -1) * this.targetPosition / this.step; this.onChangeCurrentIndex(Math.round(actualIndex)); var lowerIndex = Math.floor(actualIndex); var higherIndex = Math.ceil(actualIndex); var lowerDiff = actualIndex - lowerIndex; var higherDiff = actualIndex - higherIndex; if (!is.und(this.lowerIndex) && this.lowerIndex != lowerIndex && this.lowerIndex != higherIndex) { this.setStyle(this.lowerIndex, 1, true); } if (!is.und(this.higherIndex) && this.higherIndex != lowerIndex && this.higherIndex != higherIndex) { this.setStyle(this.higherIndex, -1); } if (lowerIndex >= 0 && lowerIndex < this.sliderLength) { this.setStyle(lowerIndex, lowerDiff, true); this.lowerIndex = lowerIndex; } if (higherIndex >= 0 && higherIndex < this.sliderLength) { this.setStyle(higherIndex, higherDiff); this.higherIndex = higherIndex; } var transformValue = 'translate' + (this.o.vertical ? 'Y' : 'X') + '(' + this.targetPosition + 'px)'; this.msTrack.style[transformProperty] = transformValue; }, onDown: function (ev) { if (this.enabled && !this.pointerActive) { var event = normalizeEvent(ev); this.pointerActive = true; this.pointerId = event.id; this.pointerLastX = this.pointerCurrentX = event.x; this.pointerLastY = this.pointerCurrentY = event.y; this.trackingPoints = []; this.addTrackingPoint(this.pointerLastX, this.pointerLastY); if (this.animateInstance) this.animateInstance.stop(); } }, onMove: function (ev) { if (this.enabled && this.pointerActive) { this.pointerMoved = true; ev.preventDefault(); var event = normalizeEvent(ev); if (event.id === this.pointerId) { this.pointerCurrentX = event.x; this.pointerCurrentY = event.y; this.addTrackingPoint(this.pointerLastX, this.pointerLastY); this.requestTick(); } } }, onUp: function (ev) { if (this.enabled && this.pointerActive) { var event = normalizeEvent(ev); if (event.id === this.pointerId) { var slide = ev.target; if (this.msTrack.contains(slide)) { while (!slide.matches('.ms-slide, .ms-track')) { slide = slide.parentNode; } } var index = Array.prototype.indexOf.call(this.msSlides, slide); if (!this.pointerMoved) { if (index !== -1) { this.currentIndex = index; this.updateSlider(); } } this.stopTracking(index); } } }, onResize: function () { // this.initValues(); }, stopTracking: function (index) { this.pointerActive = false; if (this.pointerMoved || index === -1) { this.pointerMoved = false; this.addTrackingPoint(this.pointerLastX, this.pointerLastY); this.startDecelAnim(); } }, addTrackingPoint: function (x, y) { var time = Date.now(); while (this.trackingPoints.length > 0) { if (time - this.trackingPoints[0].time <= 100) { break; } this.trackingPoints.shift(); } this.trackingPoints.push({ x: x, y: y, time: time }); }, updateAndRender: function () { var pointerChange = this.o.vertical ? this.pointerCurrentY - this.pointerLastY : this.pointerCurrentX - this.pointerLastX; this.targetPosition += pointerChange * this.o.multiplier; if (this.o.bounceCoefficient) { var diff = this.checkBounds(); if (diff !== 0) { this.targetPosition -= pointerChange * dragOutOfBoundsMultiplier(diff) * this.o.multiplier; } } else { this.checkBounds(true); } this.renderTarget(); this.pointerLastX = this.pointerCurrentX; this.pointerLastY = this.pointerCurrentY; this.ticking = false; }, requestTick: function () { if (!this.ticking) { requestAnimationFrame(this.updateAndRender.bind(this)); } this.ticking = true; }, checkBounds: function (restrict) { var diff = 0; if (this.boundMin !== undefined && this.targetPosition < this.boundMin) { diff = this.boundMin - this.targetPosition; } else if (this.boundMax !== undefined && this.targetPosition > this.boundMax) { diff = this.boundMax - this.targetPosition; } if (restrict) { if (diff !== 0) { this.targetPosition = diff > 0 ? this.boundMin : this.boundMax; } } return diff; }, startDecelAnim: function () { var firstPoint = this.trackingPoints[0]; var lastPoint = this.trackingPoints[this.trackingPoints.length - 1]; var positionOffset = this.o.vertical ? lastPoint.y - firstPoint.y : lastPoint.x - firstPoint.x; var timeOffset = lastPoint.time - firstPoint.time; var D = timeOffset / 15 / this.o.multiplier; this.decVel = positionOffset / D || 0; var newTargetPosition = this.targetPosition + (this.decVel * 12); var newTargetPositionOffset = newTargetPosition % this.step; newTargetPosition = newTargetPosition - newTargetPositionOffset; if (Math.abs(newTargetPositionOffset) > this.step / 2) { newTargetPosition += (newTargetPositionOffset > 0 ? 1 : -1) * this.step; } this.updateSlider(newTargetPosition); }, fixCurrentIndex: function () { if (this.o.loop) { if (this.currentIndex < this.o.loop) { this.currentIndex = this.sliderLength - this.o.loop + (this.currentIndex - this.o.loop); } else if (this.currentIndex > this.sliderLength - this.o.loop - 1) { this.currentIndex = this.currentIndex + this.o.loop * 2 - this.sliderLength; } } }, updateSlider: function (newTargetPosition, initial) { if (is.und(newTargetPosition)) { newTargetPosition = (this.o.reverse ? 1 : -1) * this.currentIndex * this.step; } else { this.currentIndex = (this.o.reverse ? 1 : -1) * newTargetPosition / this.step; } this.fixCurrentIndex(); if (newTargetPosition !== this.targetPosition) { this.updateClassnames(); this.animateTarget(newTargetPosition, initial); } }, updateClassnames: function () { if (this.prevEl) { if (this.currentIndex === 0) { this.prevEl.classList.add('ms-first'); } else { this.prevEl.classList.remove('ms-first'); } } if (this.nextEl) { if (this.currentIndex === this.sliderLength - 1) { this.nextEl.classList.add('ms-last'); } else { this.nextEl.classList.remove('ms-last'); } } }, animateTarget: function (newTargetPosition, initial, back) { if (this.animateInstance) this.animateInstance.stop(); var _ = this; var from = this.targetPosition; var to = newTargetPosition; this.animateInstance = animate(function(progress) { _.targetPosition = to > from ? from + ((to - from) * progress) : from - ((from - to) * progress); // 0 - ((0 - -2100) * progress) var sliderMin = _.o.reverse ? 0 : -(_.sliderLength - 1) * _.step; var sliderMax = _.o.reverse ? (_.sliderLength - 1) * _.step : 0; if (!back && !_.o.loop && _.o.bounceCoefficient && ( ( _.targetPosition > sliderMax && _.targetPosition > sliderMax + Math.min((to - sliderMax) * _.o.bounceCoefficient, _.o.bounceMax) ) || ( _.targetPosition < sliderMin && _.targetPosition < sliderMin - Math.min(-(to - sliderMin) * _.o.bounceCoefficient, _.o.bounceMax) ) ) ) { _.animateInstance.stop(); _.animateTarget(_.targetPosition < sliderMin ? sliderMin : sliderMax, false, true); _.currentIndex = _.targetPosition < sliderMin ? 0 : _.sliderLength - 1; } else { _.renderTarget(); } }, initial ? 0 : 500, function(t) { return t * (2 - t); }); }, onChangeCurrentIndex: function (index) { var currentIndex = this.o.loop ? index - this.o.loop : index; currentIndex = currentIndex === this.sliderLength - this.o.loop * 2 ? 0 : currentIndex; if (is.fnc(this.o.change) && currentIndex !== this.lastCurrentIndex) { this.o.change(currentIndex, this.lastCurrentIndex); this.lastCurrentIndex = currentIndex; } }, getCurrentIndex: function () { return this.o.loop ? this.currentIndex - this.o.loop : this.currentIndex; }, enable: function () { this.enabled = true; }, disable: function () { this.enabled = false; } }; // Utils var is = { arr: function (a) { return Array.isArray(a); }, str: function (a) { return typeof a === 'string'; }, und: function (a) { return typeof a === 'undefined'; }, fnc: function(a) { return typeof a === 'function' } }; function stringToHyphens(str) { return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); } function getCSSValue(el, prop) { if (prop in el.style) { return getComputedStyle(el).getPropertyValue(stringToHyphens(prop)) || '0'; } } var t = 'transform'; var transformProperty = (getCSSValue(document.body, t) ? t : '-webkit-' + t); function buildSlide(value) { return '
  • ' + value + '
  • '; } function extendSingle(target, source) { for (var key in source) target[key] = is.arr(source[key]) ? source[key].slice(0) : source[key]; return target; } function extend(target) { if (!target) target = {}; for (var i = 1; i < arguments.length; i++) extendSingle(target, arguments[i]); return target; } function animate(step, duration, easing) { if (duration) { var start = performance.now(); var timer = null; var stopped = false; var animation = function (t) { var progress = (t - start) / duration; if (progress < 0) progress = 0; if (progress > 1) progress = 1; if (is.fnc(easing)) progress = easing(progress); step(progress); if (progress !== 1 && !stopped) timer = requestAnimationFrame(animation); }; timer = requestAnimationFrame(animation); return new function() { this.stop = function() { if (timer) cancelAnimationFrame(timer); stopped = true; }; }; } else { step(1); } } /* eslint-disable */ function getPassiveSupported() { var passiveSupported = false; try { var options = Object.defineProperty({}, 'passive', { get: function get() { passiveSupported = true; } }); window.addEventListener('test', null, options); } catch (err) {} getPassiveSupported = function () { return passiveSupported; }; return passiveSupported; } /* eslint-enable */ function normalizeEvent(ev) { if (ev.type === 'touchmove' || ev.type === 'touchstart' || ev.type === 'touchend') { var touch = ev.targetTouches[0] || ev.changedTouches[0]; return { x: touch.clientX, y: touch.clientY, id: touch.identifier }; } else { return { x: ev.clientX, y: ev.clientY, id: null }; } } function dragOutOfBoundsMultiplier(val) { return 0.000005 * Math.pow(val, 2) + 0.0001 * val + 0.55; } function getCurrentValue(values, diff, lower) { var lowerValue = values[0]; var centerValue = values[1]; var higherValue = values[2] || lowerValue; var diffValue = lower ? centerValue - lowerValue : centerValue - higherValue; return lower ? centerValue - diffValue * diff : centerValue + diffValue * diff; } return extend(MomentumSlider, { extend: extend, transformProperty: transformProperty, getCurrentValue: getCurrentValue }); })));