Карусель на Vanilla.JS +11
Первое что мы сделаем — разметка для карусели, на классах, а не id, дабы можно было использовать несколько раз один и тот же модуль на странице, ну и специфичность не была 3-его порядка.
<div class="wrap">
<div class="b-carousel js-carousel">
<button class="b-carousel__prev js-carousel__prev">Prev</button>
<button class="b-carousel__next js-carousel__next">Next</button>
<div class="b-carousel__wrap js-carousel__wrap">
<div class="b-carousel__item">
<img src="..." alt="" class="b-carousel__img">
</div>
<div class="b-carousel__item">
<img src="..." alt="" class="b-carousel__img">
</div>
<div class="b-carousel__item">
<img src="..." alt="" class="b-carousel__img">
</div>
<div class="b-carousel__item">
<img src="..." alt="" class="b-carousel__img">
</div>
</div>
</div>
</div>
У нас есть:
- Блок — обертка для карусели, который скрывает все, что вылезает за его рамки;
- Контейнер для самих слайдов, которые будут располагаться в строчку, с помощью flexbox, можем себе это позволить;
- Блоки слайдов, которые скрывают все, что вылезает за их границы;
- Блок – хелпер для выравнивания картинки по вертикали. И, внутри него уже будем располагать картинки.
В CSS используем БЭМ нотификацию, ибо не засоряем глобальную область. Сделаем его чуть-чуть адаптивным.
.b-carousel {
width: 100%;
overflow: hidden;
position: relative;
box-sizing: border-box;
border: 1px solid;
}
.b-carousel__prev,
.b-carousel__next {
position: absolute;
top: 50%;
left: 20px;
width: 50px;
height: 50px;
background: #fff;
transform: translateY(-50%) translateZ(0);
cursor: pointer;
text-indent: 100%;
white-space: nowrap;
overflow: hidden;
z-index: 3;
}
.b-carousel__next {
left: auto;
right: 20px;
}
.b-carousel__wrap {
display: flex;
transition: transform .5s;
will-change: transform;
position: relative;
z-index: 1;
height: 100%;
}
.b-carousel__item {
flex: 0 0 100%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.b-carousel__img {
width: 100%;
display: block;
}
Transform: translateZ(0) у контролов — хак для вынесения их на отдельный композитный слой, чтобы при смещении контейнера со слайдами не было перерисовки кнопок. А у обертки свойство will-change. Оно для броузера, чтобы он знал, что с блоком будут происходить какие-то действия.
Да начнем писать код. Создадим функцию, которая принимает объект с параметрами:
function Carousel(setting) {
/* Scope privates methods and properties */
let privates = {};
/* Privates properties */
privates.setting = setting;
privates.sel = {
"main": document.querySelector(privates.setting.main),
"wrap": document.querySelector(privates.setting.wrap),
"children": document.querySelector(privates.setting.wrap).children,
"prev": document.querySelector(privates.setting.prev),
"next": document.querySelector(privates.setting.next)
};
privates.opt = {
"position": 0,
"max_position": document.querySelector(privates.setting.wrap).children.length
};
// Control
if(privates.sel.prev !== null) {
privates.sel.prev.addEventListener('click', () => {
this.prev_slide();
});
}
if(privates.sel.next !== null) {
privates.sel.next.addEventListener('click', () => {
this.next_slide();
});
}
}
Методы управления каруселью:
/* Public methods */
// Prev slide
this.prev_slide = () => {
--privates.opt.position;
if(privates.opt.position < 0) {
privates.sel.wrap.classList.add('s-notransition');
privates.opt.position = privates.opt.max_position - 1;
}
privates.sel.wrap.style["transform"] = `translateX(-${privates.opt.position}00%)`;
};
// Next slide
this.next_slide = () => {
++privates.opt.position;
if(privates.opt.position >= privates.opt.max_position) {
privates.opt.position = 0;
}
privates.sel.wrap.style["transform"] = `translateX(-${privates.opt.position}00%)`;
};
Методы next_slide и prev_slide будут двигать обертку с помощью transform, дабы не было перерисовки блока, и анимация происходила на GPU.
Делаем карусель:
new Carousel({
"main": ".js-carousel",
"wrap": ".js-carousel__wrap",
"prev": ".js-carousel__prev",
"next": ".js-carousel__next"
});
Это все! Меньше кода — меньше трафика (jQuery 3.2 ~85kB). Контроль над параметрами и можем использовать несколько раз на странице.
Демо:
Если понравилась статья, то в скором времени будет продолжение с цикличной анимацией, touch-событиями и еще многими вкусными плюшками!
Комментарии (18)
psFitz
25.04.2017 14:04Зачем нам очередные велосипеды? Есть же swiper.
iGetPass
25.04.2017 17:21-1Тебе не лень тащить в зависимостях jQuery, который весит 85kB?
psFitz
26.04.2017 09:17Сначала советую разобраться в вопросе, потом умничать, есть много либ без jQuery, так же ничего против jq не имею, если в проекте есть jq — специально ищу либу на нём.
https://github.com/nolimits4web/Swiper/tree/master/dist/jsiGetPass
26.04.2017 12:32+1Ты когда-нибудь сам что-нибудь писал? Или не разбираешься в профессии и постоянно берёшь готовые либы и фреймворки?
Этот swiper, который без jQ весит 94.2kB в сжатом состоянии. Ты понимаешь сколько человек на плохом интернет соединении будет ждать его подгрузку? Пусть с http/2 && сверхбыстрого CDN && gzip'нутого. Это все время на загрузку и распаковку, когда вместо 94.2 и самого вызова ты можешь написать 5kB, который будет работать, как тебе нужноpsFitz
27.04.2017 10:17Давайте не будем судить кто и как разбирается в профессии, во 1, есть разные сайты и не везде надо писать все свое т.к. время ограничено бюджетом, так что не надо этих розовых мечтаний. Во 2, я уверен, что под конец проекта у вас наберется своих наработок на тех же 94kB, только они не будут протестированы сообществом и скорее всего в них будет куча багов.
Так же никто не мешает зайти в сорцы и вытащить себе только слайдер.forgetable
27.04.2017 10:21В этом и суть. Мне кажется, нужно стараться использовать своё и как можно меньше библиотек, если занимаешься серьёзными проектами. Если фрилансить, или поджимает время – для этого есть библиотеки.
iGetPass
27.04.2017 17:40Работай с почасовой оплатой!
Ты один раз написал хороший модуль, написал тесты к нему, и все. По-хорошему для такого модуля — это часа 2psFitz
28.04.2017 09:06Что за бред? Клиенту пофиг написал ты свой плагин или взял со стороны, ему главное меньше заплатить, исключением отдельные случаи, да и никто тебе не мешает дернуть сорцы любого плагина и вытащить нужное. Что за собрание анонимных велосипедистов?
iGetPass
28.04.2017 13:23Клиенту нужна большая конверсия, в большинстве. Один из факторов большой конверсии — быстрая загрузка страницы => меньший трафик
forgetable
У подобных каруселей всегда хочется, чтобы при переключении с последнего на первый карусель не двигалась назад, а проходила вперёд.
iGetPass
Согласен, это будет в следующей статье =)
forgetable
Здорово. Большой респект за отсутствие jQuery, кстати
iGetPass
Мне тоже нравится, не особо jQuery сегодня в зеленых броузерах нужен и в IE 8+