Власний слайдер зображень на jQuery

image
Почну з того, що дана стаття написана з метою розповісти про те, яким чином створюється слайдер прокрутки зображень для веб-сторінок. Ця стаття ні в якому разі не несе повчальний характер, вона лише служить прикладом того, як можна реалізувати наш об'єкт розгляду. Код, наданий у цій статті ви можете використовувати як якийсь шаблон для подібних розробок, сподіваюся, що мені вдасться досить докладно і доступно донести до читача всю суть мною написаного.

А тепер до справи, не так давно мені потрібно було поставити слайдер на один сайт, але пошукавши в інтернеті готові скрипти я не знайшов нічого путнього, т. к. якісь працювали не так, як мені потрібно, а інші і зовсім не запускалися без помилок в консолі. Використовувати jQuery – плагіни для слайдера мені здалося занадто нецікавим, т. к. я цим хоч і вирішу завдання, але у мене не залишиться розуміння про роботу цього механізму, так і використовувати плагін заради одного слайдера не дуже-то й оптимально. Розбиратися в кривих скриптах мені також було не дуже-то і полювання, тому я і вирішив написати свій власний скрипт для слайдера, який сам і размечу як мені потрібно.
Для початку нам необхідно визначитися з логікою роботи самого слайдера, а потім вже приступати до реалізації, на цьому етапі дуже важливо чітке розуміння роботи цього механізму, тому що без нього нам не написати коду, що працює в точності так, як ми хочемо.
Головним об'єктом у нас буде viewport, тобто блок, в якому ми будемо бачити, як крутяться наші картинки, в ньому у нас буде slidewrapper, це буде нашим блоком, що містить в собі всі зображення, збудованими в одну лінію, і який буде змінювати свою позицію всередині самого viewport.
image
Далі, з боків всередині viewport, вертикально посередині, будуть розташовуватися кнопки назад і вперед, при натисканні на які ми також будемо міняти позицію нашого slidewrapper щодо viewport, тим самим викликаючи ефект гортання картинок. І, нарешті, останнім об'єктом будуть наші кнопки навігації, що знаходяться в нижній частині viewport.
image
При кліці по них, ми просто будемо дивитися на порядковий номер цієї кнопочки і рухати на потрібний нам слайд знову ж таки шляхом зсуву slidewrapper (зміщення буде здійснюватися через зміну css-властивості transform, значення якого постійно обчислюватися).
Думаю, логіка роботи всієї цієї справи повинна бути зрозумілою після вище викладеного мною, але якщо все ж десь виникли непорозуміння, то нижче в коді все проясниться, потрібно лише трохи терпіння.
А тепер давайте писати! Насамперед відкриємо наш index-файл і пропишемо туди потрібну нам розмітку:
<div id="block-for-slider">
<div id="viewport">
<ul id="slidewrapper">
<li class="slide"><img src="img/1.jpg" alt="1" class="slide-img"></li>
<li class="slide"><img src="img/2.jpg" alt="2" class="slide-img"></li>
<li class="slide"><img src="img/3.jpg" alt="3" class="slide-img"></li>
<li class="slide"><img src="img/4.jpg" alt="4" class="slide-img"></li>
</ul>
</div>
</div>

Як бачимо, нічого складного, block-for-slider служить як раз таки блоком, в який наш слайдер буде поміщений усередині нього вже сам viewport, в якому знаходиться наш slidewrapper, він же вкладений список, тут li є слайдами, а img – картинками всередині них. Прошу звернути увагу на те, що всі картинки повинні бути одного розміру або, хоча б, пропорцій, інакше слайдер буде криво виглядати, тому що його розміри на пряму залежать від пропорцій зображення.
Тепер нам необхідно стилізувати все це справа, звичайно стилі особливо не коментують, але я вирішив все-таки загострити на цьому увагу, щоб в подальшому не ставалося непорозуміння.
body {
margin: 0;
padding: 0;
}

#block-for-slider {
width: 800px;
margin: 0 auto;
margin-top: 100px;
}

#viewport {
width: 100%;
display: table;
position: relative;
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}

#slidewrapper {
position: relative;
width: calc(100% * 4);
top: 0;
left: 0;
margin: 0;
padding: 0;
-webkit-transition: 1s;
-o-transition: 1s;
transition: 1s;
-webkit-transition-timing-function: ease-in-out;
-o-transition-timing-function: ease-in-out;
transition-timing-function: ease-in-out;
}

#slidewrapper ul, #slidewrapper li {
margin: 0;
padding: 0;
}

#slidewrapper li {
width: calc(100%/4);
list-style: none;
display: inline;
float: left;
}

.slide-img {
width: 100%;
}

Почнемо з block-for-slider, це, повторюся, наш блок на сторінці, який ми відведемо під слайдер, його висота буде залежати від його ширини і від пропорцій нашого зображення, т. к. viewport займає всю ширину block-for-slider, то і сам slide має таку ж ширину, а, відповідно, і картинка всередині нього змінює свою висоту в залежності від ширини (пропорції). Цей елемент на своїй сторінці я розташував горизонтально посередині, зверху відступив 100px, зробивши його позицію більш зручною для прикладу.
Елемент viewport, як вже говорилося, займає всю ширину нашого block-for-slider, він має властивість overflow:hidden, воно дозволить нам приховати нашу стрічку зображень, яка вилазить за рамки viewport.
image
Наступне css властивість — user-select:none, дозволяє позбавиться від синього виділення окремих елементів слайдера при численних кліках по кнопках.
Переходимо до slidewrapper, чому ж position:relative, а не absolute? Все дуже просто, бо якщо ми оберемо другий варіант, то при властивості viewport overflow:hidden нам не здасться рівно нічого, т. к. сам viewport не стане підлаштовуватися під висоту slidewrapper, з-за чого буде мати height:0. Чому ширина має таке значення і навіщо ми її взагалі задаємо? Справа в тому, що наші слайди будуть мати ширину, рівну 100% від viewport, а щоб розставити їх в лінію, нам потрібно місце, де вони будуть стояти, таким чином ширина slidewrapper має дорівнювати 100% ширини viewport, помноженої на кількість слайдів (в моєму випадку на 4). Що стосується transition і transition-timing-function, 1s означає, що зміна зміна положення slidewrapper буде відбуватися протягом 1 секунди ми і будемо спостерігати, а ease-in-out – вид анімації, при якому вона спочатку йде повільно, прискорюється до середини, а потім знову сповільнюється, тут ви можете встановити значення за своїм розсудом.
Наступний блок властивостей задає slidewrapper та його дочірніх елементів нульові відступи, тут коментарі зайві.
Далі ми стилізуємо наші слайди, їх ширина повинна дорівнювати ширині viewport, але оскільки вони знаходяться в slidewrapper, ширина якого дорівнює ширині viewport помноженої на кількість слайдів щоб отримати ширину viewport знову, нам потрібно 100% від ширини slidewrapper поділити на кількість слайдів (в моєму випадку, знову ж таки, на 4). Після перетворимо їх на малі елементи за допомогою display:inline і задамо обтікання зліва, додавши властивість float:left. Про list-style:none я можу сказати, що використовую його для того, щоб прибрати дефолтний маркер у li, в більшості випадків є певним стандартом.
slide-img все просто, картинка буде займати всю ширину slide, slide підлаштується під її висоту, slidewrapper підлаштується під висоту slide, а висота viewport у свою чергу прийме значення висоти slidewrapper, таким чином висота нашого слайдера буде залежати від пропорцій зображення і розмірів блоку, наданого під слайдер, про що я вже писав вище.
Думаю, на цьому ми зі стилями розібралися, зробимо поки що простий показ слайдів без кнопок, а після того, як переконаємося що він справно працює, додамо і стилізуємо їх.
Відкриємо наш js-файл, в якому і буде код слайдера, не забудьте підключити jQuery, т. к. писати ми будемо за допомогою цього фреймворку. Я, до речі, на момент написання статті, використовую версію jQuery 3.1.0. Сам же файл зі скриптом необхідно підключити в самому кінці тега body, оскільки ми будемо працювати з DOM-елементами, які необхідно ініціалізувати в першу чергу.
Поки що нам потрібно оголосити пару змінних, одна буде зберігати в собі номер слайда, який ми бачимо в певний момент часу у viewport, я назвав її slideNow, а друга буде зберігати кількість цих самих слайдів, це slideCount.
var slideNow = 1;
var slideCount = $('#slidewrapper').children().length);

Змінної slideNow необхідно задати початкове значення 1, т. к. при завантаженні сторінки ми, виходячи з нашої розмітки, будемо бачити перший слайд у viewport.
slideCount ми помістимо кількість дочірніх елементів slidewrapper, тут все логічно.
Далі необхідно створити функцію, яка як раз-таки і буде відповідати за перемикання слайдів справа наліво, оголосимо її:
function nextSlide() {
}

Її ми будемо викликати в основному блоці нашого коду, до якого ми ще доберемося, а поки що скажемо нашої функції, що їй потрібно робити:
function nextSlide() {
if (slideNow == slideCount || slideNow <= 0 || slideNow > slideCount) {
$('#slidewrapper').css('transform', 'translate(0, 0)');
slideNow = 1;
} else {
translateWidth = -$('#viewport').width() * (slideNow);
$('#slidewrapper').css({
'transform': 'translate(' + translateWidth + 'px, 0)',
'-webkit-transform': 'translate(' + translateWidth + 'px, 0)',
'-ms-transform': 'translate(' + translateWidth + 'px, 0)',
});
slideNow++;
}
}

Для початку ми перевіряємо, чи знаходимося ми зараз на останньому слайді нашої стрічки? Для цього ми беремо кількість всіх наших слайдів за допомогою $('#slidewrapper').children().length та звіряємо його з номером нашого слайда, якщо вони виявляються рівними, то це означає, що нам потрібно почати показувати стрічку заново, з 1 слайда, а значить міняємо css властивість transform slidewrapper translate(0, 0), таким чином зміщуючи його в початкове положення, щоб у нашому полі зору опинився перший слайд, не забудемо також про –webkit і –ms для адекватного кроссбраузерного відображення (див. довідник з css-властивостей). Після цього не забудемо оновити значення змінної slideNow, повідомивши їй, що в полі зору перебуває слайд номер 1: slideNow = 1;
це ж умова входить перевірка на те, що номер слайда, який ми бачимо, знаходиться в межах кількості наших слайдів, якщо ж якимось чином це не виконається, то знову повернемося на 1-ий слайд.
Якщо ж перша умова не виконується, то це говорить про те, що ми на даний момент не перебуваємо не на останньому слайді, ні на якомусь неіснуючому, а значить нам необхідно перейти на наступний, зробимо ми це шляхом зсуву slidewrapper вліво на значення, рівну ширині viewport, зсув знову буде відбуватися через знайоме нам властивість translate, значення якого буде рівним 'translate(' + translateWidth + 'px, 0)', де translateWidth – відстань, на яку зміщується наш slidewrapper. До речі, оголосимо змінну спочатку нашого коду:
var translateWidth = 0;

Після переходу на наступний слайд скажімо нашому slideNow, що ми бачимо наступний за рахунком слайд: slideNow++;
На даний момент у деяких читачів може виникнути питання: чому ми не замінили $('#viewport').width() на якусь змінну, наприклад slideWidth, щоб завжди мати під рукою ширину нашого слайда? Відповідь дуже проста, якщо наш сайт адаптивний, то, выдповыдно, блок, виділений під слайдер теж адаптивний, виходячи з цього можна зрозуміти, що при зміні розмірів ширини вікна без перезавантаження сторінки (наприклад, поворот телефону на бік), ширина viewport зміниться, а, відповідно, зміниться і ширина одного слайда. В такому випадку наш slidewrapper буде зміщуватися на значення тієї ширини, яка була спочатку, а значить картинки будуть відображатися частинами або зовсім не відображатися під viewport. Записавши в нашу функцію $('#viewport').width() замість slideWidth ми змушуємо її при кожному перемиканні слайдів обчислювати ширину viewport, тим самим забезпечуючи при різкій зміні ширини екрана докрутку до потрібного нам слайда.
Втім, функцію ми написали, тепер необхідно викликати її через певний інтервал часу, інтервал ми теж можемо зберігати в змінної, щоб при бажанні його змінити, замінити лише одне значення в коді:
var slideInterval = 2000;

Час в js вказується в мілісекундах.
Тепер напишемо таку конструкцію:
$(document).ready(function () {
setInterval(nextSlide, slideInterval);
});

Тут все простіше нікуди, ми через конструкцію $(document).ready(function () {}) говоримо про те, що такі дії необхідно виконувати після повної завантаження документа. Далі ж ми просто викликаємо функцію nextSlide з інтервалом, рівним slideInterval, за допомогою вбудованої функції setInterval.
Після всіх дій, які ми виконали вище, наш слайдер повинен чудово крутитися, якщо ж у вас щось пішло не так, то проблема може бути у версії jQuery, або у неправильному підключенні будь-яких файлів. Також не слід виключати, що ви могли допустити де-небудь помилку в коді, так що можу лише порадити все перевірити.
image
Тим часом рухаємося далі, додамо до нашого слайдеру таку функцію, як зупинка прокручування при наведенні курсору, для цього нам необхідно прописати в основному блоці коду (усередині конструкції $(document).ready(function () {}) ) таку річ:
$('#viewport').hover(function(){
clearInterval(switchInterval);
},function() {
switchInterval = setInterval(nextSlide, slideInterval);
});

Щоб почати аналізувати цей код, нам потрібно знати, що таке switchInterval. По-перше, це змінна, в якій зберігається періодичний виклик функції nextSlide, просто кажучи, ми цю строчку коду: setInterval(nextSlide, slideInterval);, перетворили в цю: switchInterval = setInterval(nextSlide, slideInterval);. Після цих маніпуляцій наш основний блок коду прийняла наступний вигляд:
$(document).ready(function () {
var switchInterval = setInterval(nextSlide, slideInterval);

$('#viewport').hover(function(){
clearInterval(switchInterval);
},function() {
switchInterval = setInterval(nextSlide, slideInterval);
});
});

Тут я використовую подія hover, що означає «наведення», ця подія дозволяє відстежити той момент, коли я наводжу курсор на який-небудь об'єкт, в даному випадку на viewport.
Після наведення я очищаю інтервал, який зазначу в дужках (це наш switchInterval), далі, через кому, я пишу, що я буду робити, коли відведу курсор назад, в цьому блоці я знову присваиваю нашому switchInterval періодичний виклик функції nextSlide.
Тепер, якщо ми перевіримо, то побачимо, як наш слайдер реагує на наведення курсору, зупиняючи перемикання слайдів.
Ось і прийшов час додавати кнопки до нашого слайдеру, почнемо з кнопок вперед-назад.
Першим ділом розмітив їх:
<div id="block-for-slider">
<div id="viewport">
<ul id="slidewrapper">
<li class="slide"><img src="img/1.jpg" alt="1" class="slide-img"></li>
<li class="slide"><img src="img/2.jpg" alt="2" class="slide-img"></li>
<li class="slide"><img src="img/3.jpg" alt="3" class="slide-img"></li>
<li class="slide"><img src="img/4.jpg" alt="4" class="slide-img"></li>
</ul>

<div id="prev-next-btns">
<div id="prev-btn"></div>
<div id="next-btn"></div>
</div>
</div>
</div>

Спочатку ця розмітка може бути незрозумілою, скажу відразу, що обернув ці дві кнопки div з класом prev-next-btns просто для своєї зручності, ви можете цього не робити, результат від цього не зміниться, зараз ми додамо їм стилі і все стане ясно:
#prev-btn, #next-btn {
position: absolute;
width: 50px;
height: 50px;
background-color: #fff;
border-radius: 50%;
top: calc(50% - 25px);
}

#prev-btn:hover, #next-btn:hover {
cursor: pointer;
}

#prev-btn {
left: 20px;
}

#next-btn {
right: 20px;
}

Спочатку ми позиціонуємо наші кнопки через position:absolute, тим самим будемо вільно керувати їх становищем всередині нашого viewport, далі вкажемо розміри цих кнопок і за допомогою border-radius закругливши кути так, щоб ці кнопки перетворилися в кола. Колір їх білий, тобто #fff, а їх відступ від верхнього краю viewport буде дорівнює половині висоти цього viewport мінус половина висоти самої кнопки (в моєму випадку 25px), таким чином ми зможемо їх розташувати вертикально по центру. Далі ми вкажемо, що при наведенні на них, наш курсор зміниться на pointer і, врешті-решт, повідомимо нашим кнопок окремо, що вони повинні відступати від своїх країв на 20px, щоб ми могли їх бачити так, як нам було б зручно.
Повторюся, що стилізувати елементи сторінки ви можете так, як хочете, я лише наводжу приклад тих стилів, які я вирішив використовувати.
Після стилізації наш слайдер повинен виглядати приблизно ось так:
image
Далі, знову переходимо в наш js-файл, де ми опишемо роботу наших кнопок. Що ж, додамо ще одну функцію, вона буде показувати нам попередній слайд:
function prevSlide() {
if (slideNow == 1 || slideNow <= 0 || slideNow > slideCount) {
translateWidth = -$('#viewport').width() * (slideCount - 1);
$('#slidewrapper').css({
'transform': 'translate(' + translateWidth + 'px, 0)',
'-webkit-transform': 'translate(' + translateWidth + 'px, 0)',
'-ms-transform': 'translate(' + translateWidth + 'px, 0)',
});
slideNow = slideCount;
} else {
translateWidth = -$('#viewport').width() * (slideNow - 2);
$('#slidewrapper').css({
'transform': 'translate(' + translateWidth + 'px, 0)',
'-webkit-transform': 'translate(' + translateWidth + 'px, 0)',
'-ms-transform': 'translate(' + translateWidth + 'px, 0)',
});
slideNow--;
}
}

Вона називається prevSlide, викликатися вона буде тільки при кліку prev-btn. Спочатку робимо перевірку на те, знаходимося ми на 1-му слайді чи ні, тут ми також перевіряємо, чи не вийшов наш slideNow за межі реального диапазаона наших слайдів і, в разі, якщо якась з умов спрацює, перместимся на останній слайд, змістивши slidewrapper на потрібне нам значення. Це значення ми обчислимо за формулою: (ширина одного слайда)*(к-ть слайдів – 1), усе це беремо зі знаком мінус, т. к. зміщуємо його вліво, виходить, що viewport тепер буде показувати нам останній слайд. В кінці цього блоку нам також потрібно сказати змінної slideNow, що зараз у нашому полі зору перебуває останній слайд.
Якщо ж ми не знаходимося на першому слайді, то нам потрібно зміститися на 1 назад, для цього знову ж міняємо властивість transform у slidewrapper. Формула така: (ширина одного слайда)*(номер поточного слайда – 2), все це, знову ж таки, беремо зі знаком мінус. Але чому ж -2, а не -1, нам же потрібно переміститися як раз таки на 1 слайд назад? Справа в тому, що якщо ми знаходимося, скажімо, на 2-му слайді, то змінна x властивості transform:translate(x,0) нашого slidewrapper вже дорівнює ширині одного слайда, якщо ми йому скажемо, що від номера поточного слайда потрібно відняти 1, то знову отримаємо одиницю, на яку вже зміщений slidewrapper, тому потрібно буде зміщувати на 0 цих самих ширін viewport, а значить на slideNow — 2.
Далі ми просто віднімаємо з змінної slideNow одиницю, тим самим вказуючи на те, що бачимо вже попередній слайд.
Тепер нам залишилося внести в основний блок коду ці рядки:
$('#next-btn').click(function() {
nextSlide();
});

$('#prev-btn').click(function() {
prevSlide();
});

Тут ми просто відслідковуємо, чи було зроблено клік на наші кнопки, і в цьому випадку викликаємо потрібні нам функції, все просто і логічно.
Тепер додамо кнопки навігації по слайдах, знову повертаємося до розмітки:
<div id="block-for-slider">
<div id="viewport">
<ul id="slidewrapper">
<li class="slide"><img src="img/1.jpg" alt="1" class="slide-img"></li>
<li class="slide"><img src="img/2.jpg" alt="2" class="slide-img"></li>
<li class="slide"><img src="img/3.jpg" alt="3" class="slide-img"></li>
<li class="slide"><img src="img/4.jpg" alt="4" class="slide-img"></li>
</ul>

<div id="prev-next-btns">
<div id="prev-btn"></div>
<div id="next-btn"></div>
</div>

<ul id="nav-btns">
<li class="slide-nav-btn"></li>
<li class="slide-nav-btn"></li>
<li class="slide-nav-btn"></li>
<li class="slide-nav-btn"></li>
</ul>
</div>
</div>

Як бачимо, всередині viewport з'явився вкладений список, дамо йому идентефикатор nav-btns, усередині нього li – наші кнопки навігації, їм присвоїмо клас slide-nav-btn, втім з розміткою можна і закінчити, приступаємо до стилів:
#nav-btns {
position: absolute;
width: 100%;
bottom: 20px;
padding: 0;
margin: 0;
text-align: center;
}

.slide-nav-btn {
position: relative;
display: inline-block;
list-style: none;
width: 20px;
height: 20px;
background-color: #fff;
border-radius: 50%;
margin: 3px;
}

.slide-nav-btn:hover {
cursor: pointer;
}

image
Блоку nav-btns, в якому знаходяться наші кнопочки, даємо властивість position:absolute, для того, щоб він не розтягнув viewport по висоті, т. до. у slidewrapper властивість position:relative, ширину в 100% ми задаємо, щоб за допомогою text-align:center сцентрировать кнопки горизонтально відносно viewport, далі з допомогою властивості bottom даємо зрозуміти нашого блоку, що він від нижнього краю повинен знаходитися на відстані в 20px.
З кнопками ми робимо теж саме, що і зі слайдами, але при цьому тепер задаємо їм display:inline-block, т. к. при display:inline вони не реагують на width і height, т. к. знаходяться в абсолютно позиционированном блоці. Колір їх зробимо білий і за допомогою вже знайомого нам border-radius дамо їм форму кола. При наведенні на них змінимо вигляд нашого курсору для звичного відображення.
А тепер приступаємо до jQuery – частини:
Для початку оголосимо змінну navBtnId, в якій буде зберігатися індекс кликнутой нами кнопки:
var navBtnId = 0;

$('.slide-nav-btn').click(function() {
navBtnId = $(this).index();

if (navBtnId + 1 != slideNow) {
translateWidth = -$('#viewport').width() * (navBtnId);
$('#slidewrapper').css({
'transform': 'translate(' + translateWidth + 'px, 0)',
'-webkit-transform': 'translate(' + translateWidth + 'px, 0)',
'-ms-transform': 'translate(' + translateWidth + 'px, 0)',
});
slideNow = navBtnId + 1;
}
});

Тут ми при кліці на нашу slide-nav-btn викликаємо функцію, яка в першу чергу присвоює змінній navBtnId індекс кликнутой кнопки, тобто її порядковий номер, так як відлік починається з нуля, то якщо ми клікаємо на другу кнопку, то в navBtnId записується значення 1. Далі ми робимо перевірку, де додаємо до порядкового номера кнопки одиницю, щоб отримати таке число, ніби відлік йшов не з 0, а 1, порівнюємо це число з номером поточного слайда, якщо вони збігаються, то ми не будемо робити ніяких дій, адже потрібний слайд вже у viewport.
Якщо ж потрібний нам слайд знаходиться не в полі зору viewport, то обчислимо відстань, на яку нам потрібно зрушити slidewrapper вліво, далі міняємо значення css-властивості transform на translate ( те саме відстань у пікселях, 0). Це ми вже робили не раз, тому питань виникнути не повинно. В кінці знову зберігаємо значення поточного слайда в змінну slideNow, це значення можна обчислити, додавши до індексу кликнутой кнопки одиницю.
На цьому, власне, все, якщо щось не зрозуміло, то я залишаю посилання на jsfiddle, де буде надано весь код, написаний в матеріалі.
Спробувати на jsfiddle

Дякую за увагу!
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.