bulma-extensions
Version:
Set of extensions for Bulma.io CSS Framework
232 lines (206 loc) • 6.36 kB
JavaScript
export default class Carousel {
constructor(selector) {
// Determine click event depending on if we are on Touch device or not
this._clickEvent = ('ontouchstart' in window) ? 'touchstart' : 'click';
this.element = typeof selector === 'string' ? document.querySelector(selector) : selector;
// An invalid selector or non-DOM node has been provided.
if (!this.element) {
throw new Error('An invalid selector or non-DOM node has been provided.');
}
this.init();
}
/**
* Initiate plugin
* @method init
* @return {void}
*/
init() {
this.items = Array.from(this.element.querySelectorAll('.carousel-item'));
this.computedStyle = window.getComputedStyle(this.element);
this.previousControl = this.element.querySelector('.carousel-nav-left');
this.nextControl = this.element.querySelector('.carousel-nav-right');
this._bindEvents();
this._initOrder();
if (this.element.dataset._autoPlay && this.element.dataset._autoPlay == 'true') {
this._autoPlay(this.element.dataset.delay || 5000);
}
}
/**
* Bind all events
* @method _bindEvents
* @return {void}
*/
_bindEvents() {
if (this.previousControl) {
this.previousControl.addEventListener(this._clickEvent, (e) => {
e.preventDefault();
this._slide('previous');
if (this._autoPlayInterval) {
clearInterval(this._autoPlayInterval);
this._autoPlay(this.element.dataset.delay || 5000);
}
}, false);
}
if (this.nextControl) {
this.nextControl.addEventListener(this._clickEvent, (e) => {
e.preventDefault();
this._slide('next');
if (this._autoPlayInterval) {
clearInterval(this._autoPlayInterval);
this._autoPlay(this.element.dataset.delay || 5000);
}
}, false);
}
// Bind swipe events
this.element.addEventListener('touchstart', (e) => {
this._swipeStart(e);
});
this.element.addEventListener('mousedown', (e) => {
this._swipeStart(e);
});
this.element.addEventListener('touchend', (e) => {
this._swipeEnd(e);
});
this.element.addEventListener('mouseup', (e) => {
this._swipeEnd(e);
});
}
/**
* Initiate slides order
* @method _initOrder
* @return {void}
*/
_initOrder() {
const currentActiveItem = this.element.querySelector('.carousel-item.is-active');
const currentActiveItemPos = this.items.indexOf(currentActiveItem);
const length = this.items.length;
if (currentActiveItemPos) {
this.items.push(this.items.splice(0, currentActiveItemPos));
} else {
this.items.unshift(this.items.pop());
}
this._setOrder();
}
/**
* Update each slide order
* @method _setOrder
*/
_setOrder() {
this.items.forEach((item, index) => {
if (index !== 1) {
item.style['z-index'] = '0';
} else {
item.style['z-index'] = '1';
}
item.style.order = index;
});
}
/**
* Save current position on start swiping
* @method _swipeStart
* @param {Event} e Swipe event
* @return {void}
*/
_swipeStart(e) {
this._touch = {
start: {
x: e.clientX,
y: e.clientY
},
end: {
x: e.clientX,
y: e.clientY
}
}
}
/**
* Save current position on end swiping
* @method _swipeEnd
* @param {Event} e swipe event
* @return {void}
*/
_swipeEnd(e) {
this._touch.end = {
x: e.clientX,
y: e.clientY
}
this._handleGesture();
}
/**
* Identify the gestureand slide if necessary
* @method _handleGesture
* @return {void}
*/
_handleGesture() {
const ratio = {
horizontal: (this._touch.end.x - this._touch.start.x) / parseInt(this.computedStyle.getPropertyValue('width')),
vertical: (this._touch.end.y - this._touch.start.y) / parseInt(this.computedStyle.getPropertyValue('height'))
};
if (ratio.horizontal > ratio.vertical && ratio.horizontal > 0.25) {
this._slide('previous');
}
if (ratio.horizontal < ratio.vertical && ratio.horizontal < -0.25) {
this._slide('next');
}
}
/**
* Update slides to display the wanted one
* @method _slide
* @param {String} [direction='next'] Direction in which slide needs to move
* @return {void}
*/
_slide(direction = 'next') {
if (this.items.length) {
const currentActiveItem = this.element.querySelector('.carousel-item.is-active');
let newActiveItem;
currentActiveItem.classList.remove('is-active');
// initialize direction to change order
if (direction === 'previous') {
// Reorder items
this.items.unshift(this.items.pop());
// add reverse class
this.element.classList.add('is-reversing');
} else {
// Reorder items
this.items.push(this.items.shift());
// re_slide reverse class
this.element.classList.remove('is-reversing');
}
if (this.items.length >= 1) {
newActiveItem = this.items[1];
} else {
newActiveItem = this.items[0];
}
newActiveItem.classList.add('is-active');
this._setOrder();
// Disable transition to instant change order
this.element.classList.toggle('carousel-animated');
// Enable transition to animate order 1 to order 2
setTimeout(() => {
this.element.classList.toggle('carousel-animated');
}, 50);
}
}
/**
* Initiate autoplay system
* @method _autoPlay
* @param {Number} [delay=5000] Delay between slides in milliseconds
* @return {void}
*/
_autoPlay(delay = 5000) {
this._autoPlayInterval = setInterval(() => {
this._slide('next');
}, delay);
}
}
/**
* Initiate all DOM element containing carousel class
* @method
* @return {[type]} [description]
*/
document.addEventListener('DOMContentLoaded', function() {
var carousels = document.querySelectorAll('.carousel, .hero-carousel');
[].forEach.call(carousels, function(carousel) {
new Carousel(carousel);
});
});