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

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

Первая часть находиться здесь. Разметка карусели и её стили оставим прежними. А вот 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();
	}
};

Демо:


Это все! Меньше сторонних либ => меньший трафик для пользователя => скорейшая загрузка страницы.
Нужны ли подобные статьи на Vanilla.JS?
67%
(56)
Да, хочу!
33%
(28)
Нет, вали!

Проголосовало 84 человека. Воздержалось 28 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Поделиться с друзьями
-->

Комментарии (20)


  1. Odrin
    02.05.2017 15:54

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

    Больше костылей. Больше велосипедов. Больше времени на дебаг и багфикс. Больше времени, чтоб вспомнить через год, как это работает. Больше времени другому разработчику разобраться, как это работает.
    Uncaught ReferenceError: xDown is not defined


    1. iGetPass
      02.05.2017 16:47

      Ошибку поправил, спасибо!
      Если Вы умеете читать код и писать комментарии, то проблем ни у Вас, ни у другого разработчика не будет


      1. Odrin
        02.05.2017 17:18
        +1

        Это ошибочное мнение. Необходимость читать код (свой или чужой) — уже проблема, а лучший код тот, который не написан.


        1. forgetable
          02.05.2017 17:59

          Ошибочное мнение – считать мнение другого человека ошибочным. Я понимаю, есть такие случаи, когда ошибочность действительно очевидна, но здесь не так. К тому же, как ни крути, в среднем jQuery выполняет все действия медленнее в два раза, и пора бы уже в 2017 году отказаться от бессмысленной на данный момент библиотеки.


          1. Odrin
            02.05.2017 18:36

            Где вы увидели хотя бы одно упоминание jQuery?


            1. forgetable
              02.05.2017 18:52

              В прошлой статье был акцент на это. А в этой упоминание jQuery в том плане, что 99% плагинов пишутся на jQuery, а не на JavaScript.


              1. iGetPass
                02.05.2017 18:53
                -1

                Маленько поправлю. Пишутся с использованием jQuery, а не на чистом JS.


          1. iGetPass
            02.05.2017 18:36

            Согласен, ей не место в 2017 году. В больших проектах её надобность тоже под вопросом


  1. XAHTEP26
    02.05.2017 16:08
    +1

    Думаю статья сыровата, чтобы выкладывать ее. Надо довести код до ума.
    И перед тем как выкладывать на всеобщее обозрение — было бы неплохо самому все протестировать. Например заявленные тач-события не работают должным образом.
    Получилась карусель «Доделай сам».


    1. iGetPass
      02.05.2017 16:49

      Ошибку поправил, спасибо!
      Это не готовая либа, которую нужна взять и использовать


  1. xom9lk
    02.05.2017 16:20

    Еще бы не загружать картинки, которые пользователь не видит.
    Помню был клиент, у которого не обрабатывались картинки, редакторы вручную их тоже не обрабатывали. В итоге на главной странице сразу 200 мгб грузилось))


  1. tehSLy
    03.05.2017 12:17

    Мне вот все больше и больше непонятен смысл этих велосипедов. Для себя, саморазвития, учебных целей, да, да, и еще раз — да. Но пользоваться в продакшене этим, такое себе развлечение. Потому что, потом это разрастается до несколько k строчек кода, без документации, тестов, и прочих радостей жизни белых людей. Зато сэкономим ~60кб(хочу заметить экономим при этом на ГАЛЕРЕЕ картинок, которая будет весить явно больше, нивелируя оверхед по весу) на один единственный запрос, который потом будет грузить уже из кэша эти же скрипты.
    Насчет тормознутости jQ — нуу, я честно говоря не знаю, как же надо строить код, чтобы лагал ИМЕННО jQuery. Тут по моему проблема не в либе, а в девелопере.


    1. iGetPass
      03.05.2017 12:37

      Конечно, лучше притащить за собой МБайты кода, избыточного кода, чем написать свой велосипед и покрыть его тестами. По поводу загрузки из кэша… Любому энджайну нужно время на распарс скрипта, даже из кэша


      1. Spunreal
        03.05.2017 15:38

        Пример из жизни.
        Нужно было сделать тултип. Простенький, но с фичей одной. Библиотек как-то сходу не нашёл с этой фичей, да и зачем, когда там всё просто. Написали, Ззпустили в продакшн. Вроде сэкономили несколько килобайт трафика, но…
        Каждый месяц руководство придумывало что-то новое, тултип становился неповоротливым и сложным в понимании, т.к. изначально он не был рассчитан на всё это, а так же оброс костылями.
        Через год он стал весить больше готовой библиотеки, которая имеет несколько тысяч звёзд на гитхабе и покрывает на 100% текущие возможности самописного. Так ещё и поддерживать и изменять готовый проще, т.к. его написал человек с куда большим опытом во фронтенде, чем мы. К сожалению, перевести тултип на плагин так и не вышло, т.к. я ушёл оттуда. Но вот мне жалко ребят, которые тратят время и нервы на разбор сложной и непонятной логики вместо включения опций при инициализации плагина. И да, багов там в разы меньше, т.к. репозиторий довольно живой и активно всё исправляется с подсказками сообщества.


        1. iGetPass
          03.05.2017 15:50

          Согласен, что если есть готовая либа, которая покрывает тз, то лучше её использовать.
          Но если Вам нужна простенькая карусель, то проще и быстрее написать своё и покрыть её тестами


          1. Spunreal
            03.05.2017 17:31

            Требовался тоже очень простенький тултип изначально :)


          1. bohdan4ik
            03.05.2017 17:52

            Но если Вам нужна простенькая карусель, то проще и быстрее написать своё и покрыть её тестами

            Проще и быстрее взять простенькую и готовую карусель на 7.4КБ (после минификации, но до gzip), которая уже написана, покрыта тестами, документирована и имеет гораздо меньше багов.


            Для 99% типовых задач есть 100500 библиотек на всякий вкус и смысла их писать самому, кроме как самообучение (за счёт работодателя, как предлагаете вы) — нет.


            Как объект для статьи — карусель подходит отлично, достаточно много разных знаний нужно иметь, чтобы оно адекватно работало. И, тем не менее, статья, состоящая из листингов кода без комментариев — как-то неочень.


    1. dom1n1k
      03.05.2017 17:24

      1. Все дело в том, что на среднестатистическом современном сайте таких «да кто там заметит те лишние 60 кб!» легко может набраться несколько.
      2. Библиотеку нужно не только загрузить, но и распаковать, распарсить, откомпилировать, выполнить, проинстансить экземпляры объектов, занять память. Всё по отдельности — вроде бы копеечные затраты, но — см. пункт 1.


  1. tehSLy
    03.05.2017 13:14

    В каком месте в jQ «МБайты кода»? Локальный тест на слабенькой машинке выдал 15мс на парсинг. Лучше для кого? Сейчас даже дешевейшая китайская мобилка распарсит эти жалкие 80кб кода незаметно для пользователя, чего уж говорить о десктопах и планшетах, т.е. юзеру по большому счету фиолетово(не считая багов само собой, от которых юзер потом будет страдать). Лучше ли разработчику? Навряд ли. Если так надо, напишите готовую либу, протестированную, отдокументированную, и выливайте в опенсорс, вам только спасибо скажут (хотя конечно спорно, этих либ не пересчитать уже), а так, «жквери тяжелая и тормознутая, напишу свое, непонятное, зато родное» и спустя месяц/если придет другой разраб, то будет потрачено время и нервы с целью выяснить, как же оно все работает.


  1. lekzd
    03.05.2017 22:41

    задумываться об альтернативах и оптимизации — ок.
    переизобретать что-то для изучения — ок
    показывать это кому-то вперёд наставника — не ок
    заявлять фичи и не реализовывать — не ок
    не выкладывать код ссылкой в конце — не ок

    поэтому я проголосовал за вариант «нет», но это не значит что тебе надо валить с хабра. ты молод и можешь во многом себя попробовать, а сейчас получишь только критику, которая тебе не понравится.