slendr
Version:
A responsive & lightweight slider for modern browsers.
328 lines (322 loc) • 11.4 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var emitus = require('emitus');
var imgz = require('imgz');
function child(element, selector) {
if (!element || !selector)
return null;
return element.querySelector(selector);
}
function children(selector, parent) {
return Array.prototype.slice.call((parent || document.body).querySelectorAll(selector));
}
function onClick(element, func) {
if (!element || !func)
return;
element.addEventListener('click', (e) => {
e.preventDefault();
func(e);
}, false);
}
function transform(element, value) {
if (!element)
return;
element.style.setProperty('-webkit-transform', value);
element.style.setProperty('-moz-transform', value);
element.style.setProperty('transform', value);
}
function translateX(element, x = '0px') {
if (element)
transform(element, `translateX(${x})`);
}
function cleanClass(className) {
return className.replace(/^\./g, '');
}
const defaults = {
// Selectors
container: '.slendr',
selector: '.slendr-slides > .slendr-slide',
// Animation
animationClass: '.slendr-animate',
// Direction navs
directionNavs: true,
directionNavPrev: '.slendr-prev',
directionNavNext: '.slendr-next',
// Control navs
controlNavs: true,
controlNavClass: '.slendr-control',
controlNavClassActive: '.slendr-control-active',
// Slide
slideVisibleClass: '.slendr-visible',
slideActiveClass: '.slendr-active',
// Slideshow
slideshow: true,
slideshowSpeed: 4000,
// Keyboard
keyboard: false
};
/**
* Slendr is a responsive & lightweight slider for modern browsers.
*/
class Slendr {
constructor(options) {
this.options = options;
this.slidesLength = 0;
this.current = 0;
this.timeout = 0;
this.paused = true;
this.animating = false;
this.containerWidth = 0;
this.translationDir = 0;
this.slide = null;
this.controlNavActive = null;
this.opts = Object.assign({}, defaults, this.options);
if (typeof this.opts.container === 'string') {
const childContainer = child(document.body, this.opts.container);
if (!childContainer)
throw new Error('No container found');
this.container = childContainer;
}
else
this.container = this.opts.container;
const selectorContainer = this.opts.selector.substr(0, this.opts.selector.search(' '));
const slidesContainer = child(this.container, selectorContainer);
if (!slidesContainer)
throw new Error('No slides container found');
this.slidesContainer = slidesContainer;
this.slides = children(this.opts.selector, slidesContainer);
this.slidesLength = this.slides.length;
this.containerWidth = this.container.offsetWidth;
this.opts.animationClass = cleanClass(this.opts.animationClass);
this.opts.slideActiveClass = cleanClass(this.opts.slideActiveClass);
this.opts.slideVisibleClass = cleanClass(this.opts.slideVisibleClass);
this.opts.controlNavClass = cleanClass(this.opts.controlNavClass);
this.opts.controlNavClassActive = cleanClass(this.opts.controlNavClassActive);
this.emitter = emitus.emitus();
this.initialize();
}
/**
* Moves the current slider to the previous slide
*/
prev() {
if (!this.animating)
this.goTo(0);
}
/**
* Moves the current slider to the next slide
*/
next() {
if (!this.animating)
this.goTo(1);
}
/**
* Starts slideshow timer mode for the current slider
*/
play() {
if (!this.paused)
return;
this.opts.slideshow = true;
this.slideshow();
this.emitter.emit('play', [this.current]);
}
/**
* Pauses slideshow timer for the current slider
*/
pause() {
if (!this.opts.slideshow)
return;
clearTimeout(this.timeout);
this.paused = true;
this.animating = false;
this.opts.slideshow = false;
this.emitter.emit('pause', [this.current]);
}
/**
* Moves the current slider by slide index
*
* @param index Slide index to move
*/
move(index) {
this.goToBy(index);
}
/**
* Adds an event listener to the current slider
*
* @param eventName SlendrEvent ('move', 'next', 'prev', 'play' or 'pause')
* @param listener EmitusListener
*/
on(eventName, listener) {
this.emitter.on(eventName, listener);
}
/**
* Removes a registered event listener
*
* @param eventName SlendrEvent ('move', 'next', 'prev', 'play' or 'pause')
* @param listener EmitusListener
*/
off(eventName, listener) {
this.emitter.off(eventName, listener);
}
initialize() {
if (this.slidesLength < 2) {
if (this.slidesLength === 1) {
this.slides[0].setAttribute('data-slide-index', '0');
this.showSlideBy(0);
}
return;
}
this.container.setAttribute('data-slides-length', this.slidesLength.toString());
this.showSlideBy(0);
this.addEvents();
if (this.opts.controlNavs) {
this.controlNavActive = this.controlNavs();
if (this.controlNavActive)
this.controlNavActive(0);
}
if (this.opts.directionNavs)
this.directionNavs();
else
this.opts.directionNavs = false;
if (this.opts.keyboard)
this.keyboard();
this.loadImages();
this.slideshow();
}
goToBy(index) {
if (!this.animating && this.current !== index && (index >= 0 && index < this.slidesLength)) {
this.goTo(this.current - index < 0 ? 1 : 0, index);
}
}
goTo(direction, index = -1) {
this.animating = true;
window.clearTimeout(this.timeout);
this.showSlide(this.slides[this.current]);
if (index !== -1) {
this.current = index;
}
else {
this.current = direction === 1 ? this.current + 1 : this.current - 1;
if (this.current > this.slidesLength - 1)
this.current = 0;
if (this.current < 0)
this.current = this.slidesLength - 1;
}
this.slide = this.slides[this.current];
this.showSlide(this.slide);
this.slidesContainer.classList.add(this.opts.animationClass);
translateX(this.slidesContainer, (direction === 1 ? '-' : '') + this.containerWidth + 'px');
translateX(this.slide, (direction === 1 ? '' : '-') + this.containerWidth + 'px');
if (this.controlNavActive)
this.controlNavActive(this.current);
this.translationDir = direction;
}
slideshow() {
if (this.opts.slideshow) {
this.paused = false;
window.clearTimeout(this.timeout);
this.timeout = window.setTimeout(() => this.next(), this.opts.slideshowSpeed);
}
}
showSlideBy(index) {
for (let i = 0; i < this.slidesLength; i++)
this.showSlide(this.slides[i], index === i, index === i);
}
showSlide(slide, yes = true, cls = false) {
if (yes)
slide.classList.add(this.opts.slideVisibleClass);
else
slide.classList.remove(this.opts.slideVisibleClass);
if (cls)
slide.classList.add(this.opts.slideActiveClass);
else
slide.classList.remove(this.opts.slideActiveClass);
}
background(slide) {
const src = slide.getAttribute('data-slide-src');
if (src) {
slide.style.setProperty('background-image', `url('${src}')`);
slide.removeAttribute('data-slide-src');
}
}
loadImages() {
const slides = this.slides;
const sources = [];
for (let i = 0; i < this.slidesLength; i++) {
slides[i].setAttribute('data-slide-index', i.toString());
const src = slides[i].getAttribute('data-slide-src') || null;
if (src)
sources.push(src);
}
if (sources.length) {
imgz.Loader(sources, (image, i) => {
if (image) {
this.emitter.emit('image:load', [image, i, this.slides[i]]);
this.background(slides[i]);
}
}, (len) => this.emitter.emit('image:completed', [len]));
}
}
keyboard() {
document.addEventListener('keyup', ({ which }) => {
if (which === 37)
this.prev();
if (which === 39)
this.next();
}, false);
}
onTransitionEnd() {
this.animating = false;
this.slidesContainer.classList.remove(this.opts.animationClass);
transform(this.slidesContainer, 'none');
transform(this.slides[this.current], 'none');
this.showSlideBy(this.current);
this.emitter.emit('move', [this.translationDir, this.current, this.slide]);
this.emitter.emit(this.translationDir ? 'next' : 'prev', [this.current, this.slide]);
this.slideshow();
}
addEvents() {
window.addEventListener('resize', () => this.containerWidth = this.container.offsetWidth, false);
this.slidesContainer.addEventListener('transitionend', () => this.onTransitionEnd(), false);
}
directionNavs() {
const prevNav = child(this.container, this.opts.directionNavPrev);
const nextNav = child(this.container, this.opts.directionNavNext);
if (prevNav)
onClick(prevNav, () => this.prev());
if (nextNav)
onClick(nextNav, () => this.next());
}
controlNavs() {
const control = child(this.container, `.${this.opts.controlNavClass}`);
if (!control)
return null;
while (control.firstChild)
control.removeChild(control.firstChild);
const controlNavList = [];
const ul = document.createElement('ul');
let i = 0;
while (i < this.slidesLength) {
const el = this.createBullet(ul, i++);
if (el)
controlNavList.push(el);
}
control.appendChild(ul);
const controlNavActive = (index) => {
if (this.slidesLength > 1) {
let n = 0;
while (n < controlNavList.length) {
controlNavList[n++].classList.remove(this.opts.controlNavClassActive);
}
controlNavList[index || 0].classList.add(this.opts.controlNavClassActive);
}
};
return controlNavActive;
}
createBullet(ul, i) {
const a = document.createElement('a');
onClick(a, () => this.goToBy(i));
ul.appendChild(a);
return a;
}
}
exports.Slendr = Slendr;