Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
209 lines
6.2 KiB
JavaScript
Executable file
209 lines
6.2 KiB
JavaScript
Executable file
'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));
|