Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
209 lines
6.2 KiB
JavaScript
209 lines
6.2 KiB
JavaScript
'use strict';
|
|
|
|
/* globals GestureDetector */
|
|
/* exported Swipeable */
|
|
|
|
(function(exports) {
|
|
var Swipeable = function(container, eltSelector, options) {
|
|
this._container = container.querySelector('.swipeable-container');
|
|
this._eltSelector = eltSelector;
|
|
|
|
this.updateElements();
|
|
this._createProgress();
|
|
|
|
this._gestureDetector = new GestureDetector(this._container,
|
|
{ holdEvents: false });
|
|
this._panHandler = e => this._handlePan(e);
|
|
this._swipeHandler = e => this._handleSwipe(e);
|
|
|
|
this._container.addEventListener('tap', (e) => {
|
|
if(this._tapCallback) {
|
|
this._tapCallback(this._elts[this._activeEltIdx].id);
|
|
}
|
|
});
|
|
|
|
options = options || {};
|
|
this._elWidth = options['element-width'] || this._elts[0].offsetWidth;
|
|
this._triggerPoint = options['auto-scroll-trigger'] || this._triggerPoint;
|
|
this._minSpeed = options['min-speed'] || this._minSpeed;
|
|
};
|
|
|
|
Swipeable.prototype = {
|
|
_gestureDetector: null,
|
|
_container: null,
|
|
_eltSelector: null,
|
|
_elts: [],
|
|
_progressElts: null,
|
|
|
|
_elWidth: 0,
|
|
_triggerPoint: 0.25,
|
|
_minSpeed: 1.0,
|
|
|
|
_inTransition: false,
|
|
_offset: 0,
|
|
_activeEltIdx: 0,
|
|
|
|
_tapCallback: null,
|
|
_swipeCallback: null,
|
|
_vertSwipeCallback: null,
|
|
|
|
set ontap(callback) {
|
|
this._tapCallback = callback;
|
|
},
|
|
|
|
set onswipe(callback) {
|
|
this._swipeCallback = callback;
|
|
},
|
|
|
|
set onvertswipe(callback) {
|
|
this._vertSwipeCallback = callback;
|
|
},
|
|
|
|
enable: function() {
|
|
this._gestureDetector.startDetecting();
|
|
this.unpauseSwipe();
|
|
},
|
|
|
|
disable: function() {
|
|
this._gestureDetector.stopDetecting();
|
|
},
|
|
|
|
pauseSwipe: function() {
|
|
this._container.removeEventListener('pan', this._panHandler);
|
|
this._container.removeEventListener('swipe', this._swipeHandler);
|
|
},
|
|
|
|
unpauseSwipe: function() {
|
|
this._container.addEventListener('pan', this._panHandler);
|
|
this._container.addEventListener('swipe', this._swipeHandler);
|
|
},
|
|
|
|
updateElements: function() {
|
|
var nodeList = this._container.querySelectorAll('.swipeable > ' + this._eltSelector);
|
|
this._elts = Array.prototype.slice.call(nodeList);
|
|
},
|
|
|
|
_createProgress: function() {
|
|
if(!this._elts || !this._elts.length) {
|
|
return;
|
|
}
|
|
|
|
var progressDiv = this._container.querySelector('.swipeable-progress');
|
|
if(!progressDiv) {
|
|
return;
|
|
}
|
|
|
|
var progressFragment = document.createDocumentFragment();
|
|
this._progressElts = this._elts.map(() => {
|
|
var div = document.createElement('div');
|
|
progressFragment.appendChild(div);
|
|
return div;
|
|
});
|
|
|
|
this._progressElts[0].classList.add('swipeable-progress-active');
|
|
progressDiv.innerHTML = '';
|
|
progressDiv.appendChild(progressFragment);
|
|
},
|
|
|
|
_updateProgress: function() {
|
|
this._progressElts.forEach((elt, idx) => {
|
|
if(idx === this._activeEltIdx) {
|
|
elt.classList.add('swipeable-progress-active');
|
|
} else if(elt.classList.contains('swipeable-progress-active')) {
|
|
elt.classList.remove('swipeable-progress-active');
|
|
}
|
|
});
|
|
},
|
|
|
|
progressSelect: function progressSelect(idx, select) {
|
|
var pElt = this._progressElts[idx];
|
|
if(!pElt) {
|
|
return;
|
|
}
|
|
|
|
if(select) {
|
|
pElt.classList.add('swipeable-progress-selected');
|
|
} else if(pElt.classList.contains('swipeable-progress-selected')) {
|
|
pElt.classList.remove('swipeable-progress-selected');
|
|
}
|
|
},
|
|
|
|
_handlePan: function(event) {
|
|
if(this._inTransition) {
|
|
return;
|
|
}
|
|
|
|
if (Math.abs(event.detail.relative.dx) > Math.abs(event.detail.relative.dy)) {
|
|
this._offset += event.detail.relative.dx;
|
|
this._animate();
|
|
}
|
|
},
|
|
|
|
_handleSwipe: function(event) {
|
|
if(this._vertSwipeCallback &&
|
|
['up', 'down'].indexOf(event.detail.direction) !== -1) {
|
|
var id = this._elts[this._activeEltIdx].id;
|
|
this._vertSwipeCallback(event.detail.direction, id);
|
|
}
|
|
|
|
var elPos = Math.abs(this._offset)%this._elWidth;
|
|
var triggerPassed = elPos > this._elWidth * this._triggerPoint;
|
|
|
|
var velocity = event.detail.vx;
|
|
var fastEnough = Math.abs(velocity) > this._minSpeed;
|
|
|
|
var direction = (event.detail.direction === 'left') ? 1 : -1;
|
|
var newIdx = this._computeNewIndex(direction);
|
|
|
|
// Make sure that that the speed and pan amount are in the same direction
|
|
//var samedirection = velocity === 0 || this._offset / velocity >= 0;
|
|
|
|
if((triggerPassed || fastEnough) && newIdx !== this._activeEltIdx) {
|
|
var speed = Math.max(Math.abs(velocity), this._minSpeed);
|
|
var time = (this._elWidth - Math.abs(this._offset%this._elWidth)) / speed;
|
|
|
|
this._activeEltIdx = newIdx;
|
|
this._performScroll(time);
|
|
this._updateProgress();
|
|
if (this._swipeCallback) {
|
|
this._swipeCallback(this._activeEltIdx);
|
|
}
|
|
} else if(this._offset !== 0){
|
|
var time = Math.abs(this._offset%this._elWidth) / this._minSpeed;
|
|
this._performScroll(time);
|
|
if (this._swipeCallback) {
|
|
this._swipeCallback(this._activeEltIdx);
|
|
}
|
|
}
|
|
},
|
|
|
|
_animate: function(time) {
|
|
var handler = (e) => {
|
|
e.target.style.transition = null;
|
|
e.target.removeEventListener('transitionend', handler);
|
|
};
|
|
this._elts.forEach((el) => {
|
|
if(time) {
|
|
el.style.transition = 'transform ' + time + 'ms ease';
|
|
el.addEventListener('transitionend', handler);
|
|
}
|
|
el.style.transform = 'translate3d('+this._offset+'px,0,0)';
|
|
});
|
|
},
|
|
|
|
_performScroll: function(time) {
|
|
this._inTransition = true;
|
|
this._offset = -1 * this._activeEltIdx * this._elWidth;
|
|
this._animate(time);
|
|
setTimeout(() => { this._inTransition = false; }, time);
|
|
},
|
|
|
|
_computeNewIndex: function(direction) {
|
|
var index = this._activeEltIdx + direction;
|
|
var offBounds = index < 0 || index >= this._elts.length;
|
|
return offBounds ? this._activeEltIdx : index;
|
|
}
|
|
};
|
|
|
|
exports.Swipeable = Swipeable;
|
|
}((typeof exports === 'undefined') ? window : exports));
|