Egy olyan kép carousselt készítünk, mely automatikusan váltogatja a képeket, de lehetőségünk van az egyéni lapozásra is vagy a jobbra/balra nyilakkal, vagy pedig a képeket jelképező alsó pontokra való kattintással. Az egyedi lapozást befejezve egy meghatározott idő múlva az automatikus kép caroussel újra indul. A JS modul rendszer gyakorlása végett a képek adatai egy külön JS file-ból töltődnek be és egy függvényen keresztül állíthatjuk a képek váltakozásának gyakoriságát és a képek magasságát.
Mivel nem tudjuk, hogy hány kép fog betöltődni, ezért a változó részek (a mindenkori kép adatai és a képek
alján
megjelenő, összes képet jelképező pontok) kikommentelve jelennek meg a HTML-ben. Ezek a CSS megírása miatt
hasznosak, először az ember megírja a statikus vázat, de ezeket az elemeket a JS fogja legeneráltatni majd a
DOM-on keresztül. Szerepel még egy képszámláló is majd a bal felső sarokban, ez a mindekori képet jelzi. A
kód
alján a JS modulok miatt a script-ben most az src
mellett szerepelnie kell a
type="module"
attributumnak is:
<body>
<div class="gallery">
<div class="slider-box">
<!-- <div class="slider">
<div class="count">1 / 5</div>
<img src="./images/1.jpg" alt="Opatija">
<div class="caption">Opatija</div>
</div> -->
</div>
<span class="left-arrow">❮</span>
<span class="right-arrow">❯</span>
</div>
<div class="dots">
<!-- <span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span> -->
</div>
<script type="module" src="./js/app.js"></script>
</body>
A szokásos boilerplate kód után
* {
margin: 0;
padding: 0;
}
*, *::before, *::after {
box-sizing: inherit;
}
html {
box-sizing: border-box;
}
body {
min-height: 100vh;
font-family: sans-serif;
background-color: rgb(169,169,169);
}
a képet befogadó parents div-re (.slider
) ráírjuk a position: relative;
tulajdonságot,
hogy a caption-t és a count-et ehhez képest tudjuk abszolut pozicionálni:
.gallery {
position: relative;
display: flex;
}
.slider {
height: 90vh;
width: 100vw;
text-align: center;
overflow: hidden;
position: relative;
}
A képek váltogatását a szokásos display block és none párossal oldjuk meg egy egyszerű animáció kiséretében:
.show {
display: block;
animation-name: show-in;
animation-duration: 2s;
}
.hide {
display: none;
}
@keyframes show-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
Soha ne feledjük a képet a szülő elemhez képest valamelyik irányban kifeszíteni, mert egyedül ez garantálja csak, hogy a kép a befoglaló elemen belül marad:
img {
width: 100%;
}
A kép feliratot, a kép számolást és a nyilakat abszolút pozicionálással állítjuk a megfelelő helyre.
.caption {
position: absolute;
bottom: 2rem;
left: 50%;
transform: translate(-50%);
background-color: rgba(0,0,0,.4);
color: #ffffff;
padding: 0.5rem 1.5rem;
font-size: 1.5rem;
}
.count {
position: absolute;
top: 0;
padding: 1rem;
font-size: 1.2rem;
color: #ffffff;
background-color: rgba(0,0,0,.3);
}
.left-arrow, .right-arrow {
position: absolute;
cursor: pointer;
padding: 1rem;
color: white;
font-size: 2rem;
line-height: 2rem;
font-weight: bold;
top: 50%;
margin-top: -2rem;
transition: 0.6s;
}
.left-arrow:hover, .right-arrow:hover {
background-color: rgba(0,0,0,.6);
}
.left-arrow {
left: 0;
border-radius: 0 3px 3px 0;
}
.right-arrow {
right: 0;
border-radius: 3px 0 0 3px;
}
A végére már csak az alsó pontok pozicionálása marad:
.dots {
text-align: center;
margin-top: 1rem;
}
.dot {
display: inline-block;
width: 1rem;
height: 1rem;
border-radius: 50%;
background-color: #ececec;
cursor: pointer;
margin-right: 0.7rem;
transition: 0.6s;
}
.dot:hover {
background-color: rgba(0,0,0,.3);
}
.dot-showed {
background-color: rgba(0,0,0,.3);
}
A képek adatai egy tömbben vannak, azon belül képenként egy objektumban, mintha egy adatbázisból kaptuk volna
meg
a lekérdezett adatokat. Mivel csak egy értéket(tömböt) akarunk exportálni, ezért most az
export default
parancsot használjuk:
const pictures = [
{
url: './images/1.jpg',
caption: 'Opatija, tengerparti sétány',
alt: 'Opatija, tengerparti sétány'
},
{
url: './images/2.jpg',
caption: 'Vrsar, Isztria',
alt: 'Vrsar, Isztria'
},
{
url: './images/3.jpg',
caption: 'Kilátás az omisi erődből',
alt: 'Kilátás az omisi erődből'
},
{
url: './images/4.jpg',
caption: 'Krka vízesés',
alt: 'Krka vízesés'
},
{
url: './images/5.jpg',
caption: 'Omis, kilátás a Mirabellből',
alt: 'Omis, kilátás a Mirabellből'
},
{
url: './images/6.jpg',
caption: 'Cesky Krumlov',
alt: 'Cesky Krumlov'
},
]
export default pictures;
Importáljuk a képek adatait a pictures.js
file-ból és azt követően felírjuk azon DOM
objektumokat,
mellyel később dolgozni fogunk valamint inicializáljuk az egyedi képváltáshoz használt index
változót, mely a mindenkori aktuális kép indexét fogja tartalmazni. Az auto
kapcsoló változó,
azt
mutatja, hogy a képek váltakozása egyedi vagy automatikus módon történik.
import pictures from './pictures.js';
const sliderBox = document.querySelector('.slider-box');
const leftArrow = document.querySelector('.left-arrow');
const rightArrow = document.querySelector('.right-arrow');
const dots = document.querySelector('.dots');
let index = 0;
let auto = true;
A kép adatok alapján először felépítjük a kép konténert .slider
, majd alatta a képeket jelképező
pontokat. Valamennyi kép hide-olva van és paraméterként kapjuk majd a slider magasságát. Ezt követően az
egyes
képek adatai (számozás, url és cím) alapján valamennyi képet hozzákapcsolunk a befoglaló szülő elemhez. A
képek
alá megrajzoljuk az őket jelképező pontokat is. A két eljárást két külön függvényben hajtjuk végre.
function loadPictures() {
for (let i = 0; i < pictures.length; i++) {
const sliderElement = document.createElement('div');
sliderElement.classList.add('slider', 'hide');
sliderElement.style.height = `${sliderViewportHeight}vh`;
sliderElement.innerHTML = `
<div class="count">${i + 1} / ${pictures.length}</div>
<img src=${pictures[i].url} alt=${pictures[i].alt}>
<div class="caption">${pictures[i].caption}</div>
`;
sliderBox.append(sliderElement);
}
}
function showDots() {
for (let i = 0; i < pictures.length; i++) {
const dotElem = document.createElement('span');
dotElem.classList.add('dot');
dots.prepend(dotElem);
}
}
A képek betöltése után meg is jelenítjük az első képet és pöttyöt.
function showPictureAutoFirst() {
slider[index].classList.remove('hide');
slider[index].classList.add('show');
dot[index].classList.add('dot-showed');
}
Induláskor automatikusan elindul a kép váltogatás, a paraméterként kapott frequency
-nek
megfelelően. A megfelelő indexű kép (ez kezdetben 0) először eltűnik, aztán a következő megjelenik. Ha a sor
végére érünk, akkor az index számítása újraindul nulláról. A timer-nek kezdetben nincs jelentősége, ez majd
akkor kap szerepet, ha egyedileg váltogatjuk a képeket és mérni fogjuk, hogy mióta nem nem történt már semmi
változás sem a nyilakon, sem pedig a pontokon keresztül, s ekkor elindítjuk majd az automatikus
képváltogatást.
function showPictureAuto() {
if (timer > frequency) {
dot[index].classList.remove('dot-showed');
slider[index].classList.remove('show')
slider[index].classList.add('hide');
index++;
if (index >= pictures.length) index = 0;
slider[index].classList.remove('hide');
slider[index].classList.add('show');
dot[index].classList.add('dot-showed');
}
}
A nyilakon és a pontokon keresztül átadunk majd egy paramétert (i), hogy melyik képre szeretnénk menni. Az
index
változó tárolja a jelenlegi kép indexét, ezt tehát először el kell tüntetnünk és utána az
i
indexű képet meg kell jelenítenünk. A nyilaknál a két végpontot egyedileg kell kezelnünk.
function showPicture(i) {
dot[index].classList.remove('dot-showed');
slider[index].classList.remove('show')
slider[index].classList.add('hide');
if (i >= pictures.length) i = 0;
if (i < 0) i = pictures.length - 1;
slider[i].classList.remove('hide');
slider[i].classList.add('show');
dot[i].classList.add('dot-showed');
index = i;
}
Mindkét nyilra és a pöttyökre is felírjuk az eseményfigyelőket. Ha egyedi kattintás történik akkor átállítjuk az automatikus figyelőt false-ra, lenullázzuk a számlálót, töröljük az automatikus képmegjelenítést és a kívánt indexü képhez navigálunk.
leftArrow.addEventListener('click', () => {
auto = false;
timer = 0;
clearInterval(showAuto);
showPicture(index - 1);
});
rightArrow.addEventListener('click', () => {
auto = false;
timer = 0;
clearInterval(showAuto);
showPicture(index + 1);
});
for (let i = 0; i < pictures.length; i++) {
dot[i].addEventListener('click', () => {
auto = false;
timer = 0;
clearInterval(showAuto);
showPicture(i);
});
}
Az egyedi paraméterezhetőség miatt az egész folyamatot becsomagoljuk egy függvénybe. Ezen belül elkezdjük a
folyamatot sorban. A timer nagyobb legyen mint a frequency, hogy automatikusan induljunk, betöltjük a
képeket,
felírjuk az éppen elkészült .slider
elemek NodeList-jét, hogy a következőkben tudjunk velük
dolgozni, felírjuk a pöttyöket, az éppen elkészült pöttyök NodeList-jét, megjelenítjük az első képet és
pöttyöket, és elindítjuk az automatikus kép váltást egy setInterval
függvénnyel. Az egyedi és
automatikus közti átmenet vizsgálatára felírunk még egy setInterval
függvényt.
function sliderIndividualAndAutomatic(frequency, sliderViewportHeight) {
let timer = frequency + 1000;
loadPictures();
const slider = document.querySelectorAll('.slider');
showDots();
const dot = document.querySelectorAll('.dot');
showPictureAutoFirst();
const showAuto = setInterval(showPictureAuto, frequency);
setInterval(() => {
if (!auto) {
timer += 1000;
showPictureAuto();
}
}, frequency)
Ezen a függvényen belül helyezkedik el az összes korábbi függvény és eseményfigyelő is.
A végén pedig két tetszőleges értékkel meghívjuk a kép váltogató függvényünket.
sliderIndividualAndAutomatic(3000, 90)