javascript

Карусель на Vanilla.JS. Часть 2

  • среда, 3 мая 2017 г. в 03:15:28
https://habrahabr.ru/post/327690/
  • Разработка веб-сайтов
  • JavaScript


Доброго времени суток. Сегодня мы добавим следующие возможности в наш слайдер:

  • Зацикленность карусели;
  • Автоматическое переключение слайдов;
  • Тач-события;
  • События мыши.

Первая часть находиться здесь. Разметка карусели и её стили оставим прежними. А вот JS перепишем.

Начнём с создания возобновляймого таймера:

function Timer(callback, delay) {
	/* Private properties */
	let timerId, start, remaining = delay;

	/* Public methods */
	this.resume = () => {
		start = new Date();
		timerId = setTimeout(() => {
			remaining = delay;
			this.resume();
			callback();
		}, remaining);
	};

	this.pause = () => {
		clearTimeout(timerId);
		remaining -= new Date() - start;
	};

	this.become = () => {
		clearTimeout(timerId);
		remaining = delay;

		this.resume();
	};

	/* Constructor */
	this.resume();
}

Теперь напишем функцию-конструктор карусели, её приватные свойства и методы:

function Carousel(setting) {

	/* Scope private methods and properties */
	let private = {},
		xDown, yDown, xUp, yUp, xDiff, yDiff;


	/* Private properties */
	private.default = {
		"touch": true,
		"autoplay": false,
		"autoplayDelay": 3000,
		"pauseOnFocus": true,
		"pauseOnHover": true
	};

	private.setting = Object.assign(private.default, setting);

	private.isAnimationEnd = true;

	private.sel = {
		"wrap": document.querySelector(private.setting.wrap),
		"children": document.querySelector(private.setting.wrap).children,
		"prev": document.querySelector(private.setting.prev),
		"next": document.querySelector(private.setting.next)
	};

	private.opt = {
		"position": 0,
		"max_position": document.querySelector(private.setting.wrap).children.length
	};


	/* Constructor */
	// Clone first elem to end wrap
	private.sel.wrap.appendChild(private.sel.children[0].cloneNode(true));


	// Autoplay
	if(private.setting.autoplay === true) {
		private.timer = new Timer(this.next_slide, private.setting.autoplayDelay);
	}


	// Control
	if(private.sel.prev !== null) {
		private.sel.prev.addEventListener('click', () => {
			this.prev_slide();
		});
	}

	if(private.sel.next !== null) {
		private.sel.next.addEventListener('click', () => {
			this.next_slide();
		});
	}

	// Touch events
	if(private.setting.touch === true) {
		private.sel.wrap.addEventListener('touchstart', private.hts, false);
		private.sel.wrap.addEventListener('touchmove', private.htm, false);
	}

	// Pause on hover
	if(private.setting.autoplay === true && private.setting.pauseOnHover === true) {
		private.sel.wrap.addEventListener('mouseenter', () => {
			private.timer.pause();
		});

		private.sel.wrap.addEventListener('mouseleave', () => {
			private.timer.become();
		});
	}

	private.hts = (e) => {
		xDown = e.touches[0].clientX;
		yDown = e.touches[0].clientY;
	};

	private.htm = (e) => {
		if ( ! xDown || ! yDown ) {
			return;
		}

		xUp = e.touches[0].clientX;
		yUp = e.touches[0].clientY;

		xDiff = xDown - xUp;
		yDiff = yDown - yUp;

		if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {
			if ( xDiff > 0 ) {
				this.next_slide();
			} else {
				this.prev_slide();
			}
		}

		xDown = 0;
		yDown = 0;
	};
}

Методы hts и htm нужны для обработки тач-событий. Hts будет вызываться при touchStart. Htm при event touchMove и в зависимости от направления mov'а вызывать метод предыдущего или последующего слайда.

Теперь напишем публичные методы:

// Prev slide
this.prev_slide = () => {
	if(!private.isAnimationEnd) {
		return;
	}

	private.isAnimationEnd = false;

	--private.opt.position;

	if(private.opt.position < 0) {
		private.sel.wrap.classList.add('s-notransition');
		private.sel.wrap.style["transform"] = `translateX(-${private.opt.max_position}00%)`;
		private.opt.position = private.opt.max_position - 1;
	}
	
	setTimeout(() => {
		private.sel.wrap.classList.remove('s-notransition');
		private.sel.wrap.style["transform"] = `translateX(-${private.opt.position}00%)`;
	}, 10);

	private.sel.wrap.addEventListener('transitionend', () => {
		private.isAnimationEnd = true;
	});

	if(private.setting.autoplay === true) {
		private.timer.become();
	}
};


// Next slide
this.next_slide = () => {
	if(!private.isAnimationEnd) {
		return;
	}

	private.isAnimationEnd = false;

	if(private.opt.position < private.opt.max_position) {
		++private.opt.position;
	}
	
	private.sel.wrap.classList.remove('s-notransition');
	private.sel.wrap.style["transform"] = `translateX(-${private.opt.position}00%)`;

	private.sel.wrap.addEventListener('transitionend', () => {
		if(private.opt.position >= private.opt.max_position) {
			private.sel.wrap.style["transform"] = 'translateX(0)';
			private.sel.wrap.classList.add('s-notransition');
			private.opt.position = 0;
		}

		private.isAnimationEnd = true;
	});

	if(private.setting.autoplay === true) {
		private.timer.become();
	}
};

Демо:
Это все! Меньше сторонних либ => меньший трафик для пользователя => скорейшая загрузка страницы.