Galériák és képek
Filmszalag-hatású, mozgó fotógaléria
Élő előnézet – a komponens teljes méretben.
Élő előnézet
HTML · CSS · JS
<section class="we-film" data-film-gallery>
<header class="we-film__header">
<span class="we-film__eyebrow">VÁLOGATOTT PILLANATOK</span>
<h2>Egy világ, <em>képekben.</em></h2>
<p>Minden fotó egy külön történet.</p>
</header>
<div class="we-film__stage">
<div class="we-film__track" data-film-track>
<article class="we-film__card">
<img src="https://images.unsplash.com/photo-1470770841072-f978cf4d019e?auto=format&fit=crop&w=900&q=85" alt="Hegyek tükröződése a tó vizében">
<div class="we-film__caption">
<span>01 / TERMÉSZET</span>
<h3>A csend tava</h3>
</div>
</article>
<article class="we-film__card">
<img src="https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=900&q=85" alt="Napsütötte, zöldellő táj">
<div class="we-film__caption">
<span>02 / SZABADSÁG</span>
<h3>A végtelen felé</h3>
</div>
</article>
<article class="we-film__card">
<img src="https://images.unsplash.com/photo-1518837695005-2083093ee35b?auto=format&fit=crop&w=900&q=85" alt="Hullámok és tengerpart">
<div class="we-film__caption">
<span>03 / VIZEK</span>
<h3>A tenger lélegzete</h3>
</div>
</article>
<article class="we-film__card">
<img src="https://images.unsplash.com/photo-1441974231531-c6227db76b6e?auto=format&fit=crop&w=900&q=85" alt="Fényben úszó erdei ösvény">
<div class="we-film__caption">
<span>04 / ERDŐK</span>
<h3>A lombok között</h3>
</div>
</article>
<article class="we-film__card">
<img src="https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?auto=format&fit=crop&w=900&q=85" alt="Magas hegycsúcsok a felhők felett">
<div class="we-film__caption">
<span>05 / HEGYEK</span>
<h3>Fent a világ tetején</h3>
</div>
</article>
</div>
<button class="we-film__nav we-film__nav--prev"
type="button" data-film-prev aria-label="Előző fotó">
‹
</button>
<button class="we-film__nav we-film__nav--next"
type="button" data-film-next aria-label="Következő fotó">
›
</button>
</div>
<footer class="we-film__footer">
<div class="we-film__progress" aria-hidden="true">
<span data-film-progress></span>
</div>
<div class="we-film__status">
<span class="we-film__number" data-film-number>01</span>
<span class="we-film__separator">/</span>
<span data-film-total>05</span>
</div>
<div class="we-film__dots" data-film-dots aria-label="Fotó kiválasztása"></div>
</footer>
</section>
.we-film {
--film-gold: #d8bd83;
--film-bg: #0b1511;
--film-card-width: clamp(230px, 36vw, 390px);
--film-gap: clamp(14px, 2.5vw, 28px);
position: relative;
width: 100%;
padding: clamp(28px, 5vw, 64px) 0 30px;
overflow: hidden;
color: #f6f2e9;
background:
radial-gradient(ellipse at 50% 42%, #294638 0%, transparent 55%),
var(--film-bg);
border: 1px solid rgba(216, 189, 131, .2);
border-radius: 24px;
font-family: Georgia, "Times New Roman", serif;
box-sizing: border-box;
}
.we-film *,
.we-film *::before,
.we-film *::after {
box-sizing: border-box;
}
.we-film__header {
position: relative;
z-index: 2;
padding: 0 20px;
margin: 0 auto 36px;
text-align: center;
}
.we-film__eyebrow {
color: var(--film-gold);
font: 700 10px/1.5 Arial, sans-serif;
letter-spacing: .24em;
}
.we-film__header h2 {
margin: 14px 0 10px;
font-size: clamp(32px, 5vw, 55px);
line-height: 1.08;
font-weight: 400;
letter-spacing: -.035em;
}
.we-film__header h2 em {
color: var(--film-gold);
font-weight: 400;
}
.we-film__header p {
margin: 0;
color: #aebbb1;
font: 14px/1.7 Arial, sans-serif;
}
.we-film__stage {
position: relative;
height: clamp(330px, 48vw, 510px);
overflow: hidden;
isolation: isolate;
}
.we-film__track {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
gap: var(--film-gap);
height: 100%;
padding: 0;
will-change: transform;
transition: transform .7s cubic-bezier(.22, .75, .25, 1);
}
.we-film__card {
position: relative;
flex: 0 0 var(--film-card-width);
height: 84%;
overflow: hidden;
background: #20372b;
border: 1px solid rgba(255, 255, 255, .18);
border-radius: 13px;
opacity: .48;
transform: scale(.84);
transition:
opacity .65s ease,
transform .65s cubic-bezier(.2, .7, .2, 1),
border-color .65s ease,
box-shadow .65s ease;
}
.we-film__card.is-active {
z-index: 1;
opacity: 1;
transform: scale(1);
border-color: rgba(216, 189, 131, .8);
box-shadow: 0 24px 60px rgba(0, 0, 0, .35);
}
.we-film__card img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 1s ease;
}
.we-film__card.is-active:hover img {
transform: scale(1.045);
}
.we-film__card::after {
content: "";
position: absolute;
inset: 20% 0 0;
background: linear-gradient(
to bottom,
transparent 0%,
rgba(5, 12, 8, .15) 30%,
rgba(5, 12, 8, .88) 100%
);
pointer-events: none;
}
.we-film__caption {
position: absolute;
z-index: 1;
right: 22px;
bottom: 25px;
left: 22px;
transform: translateY(8px);
transition: transform .6s ease;
}
.we-film__card.is-active .we-film__caption {
transform: translateY(0);
}
.we-film__caption span {
color: var(--film-gold);
font: 700 10px/1.5 Arial, sans-serif;
letter-spacing: .16em;
}
.we-film__caption h3 {
margin: 8px 0 0;
color: #fff;
font-size: clamp(22px, 3vw, 31px);
line-height: 1.15;
font-weight: 400;
}
.we-film__nav {
position: absolute;
z-index: 3;
top: 50%;
display: grid;
place-items: center;
width: 46px;
height: 46px;
padding: 0 0 4px;
color: #fff;
background: rgba(10, 22, 15, .76);
border: 1px solid rgba(216, 189, 131, .7);
border-radius: 50%;
font: 32px/1 Arial, sans-serif;
cursor: pointer;
transform: translateY(-50%);
transition: color .2s ease, background .2s ease, scale .2s ease;
}
.we-film__nav:hover {
color: var(--film-bg);
background: var(--film-gold);
scale: 1.07;
}
.we-film__nav:focus-visible,
.we-film__dots button:focus-visible {
outline: 2px solid #fff;
outline-offset: 4px;
}
.we-film__nav--prev {
left: 16px;
}
.we-film__nav--next {
right: 16px;
}
.we-film__footer {
display: grid;
grid-template-columns: 1fr auto auto;
align-items: center;
gap: 22px;
padding: 26px clamp(18px, 5vw, 55px) 0;
}
.we-film__progress {
height: 2px;
overflow: hidden;
background: rgba(255, 255, 255, .15);
}
.we-film__progress span {
display: block;
width: 20%;
height: 100%;
background: var(--film-gold);
transition: width .5s ease;
}
.we-film__status {
display: flex;
align-items: center;
gap: 7px;
color: #8d9b90;
font: 11px/1 Arial, sans-serif;
font-variant-numeric: tabular-nums;
}
.we-film__number {
color: var(--film-gold);
font-size: 16px;
}
.we-film__separator {
color: #66776b;
}
.we-film__dots {
display: flex;
align-items: center;
gap: 7px;
}
.we-film__dots button {
width: 7px;
height: 7px;
padding: 0;
background: #627166;
border: 0;
border-radius: 50%;
cursor: pointer;
transition: width .3s ease, background .3s ease;
}
.we-film__dots button.is-active {
width: 23px;
background: var(--film-gold);
border-radius: 8px;
}
@media (max-width: 600px) {
.we-film {
--film-card-width: 76vw;
--film-gap: 12px;
border-radius: 16px;
}
.we-film__header {
margin-bottom: 25px;
}
.we-film__stage {
height: 390px;
}
.we-film__card {
height: 88%;
border-radius: 10px;
}
.we-film__caption {
right: 16px;
bottom: 19px;
left: 16px;
}
.we-film__nav {
width: 38px;
height: 38px;
font-size: 27px;
}
.we-film__nav--prev {
left: 8px;
}
.we-film__nav--next {
right: 8px;
}
.we-film__footer {
grid-template-columns: 1fr auto;
gap: 14px;
padding: 22px 16px 0;
}
.we-film__dots {
grid-column: 1 / -1;
justify-content: center;
padding-top: 4px;
}
}
@media (prefers-reduced-motion: reduce) {
.we-film *,
.we-film *::before,
.we-film *::after {
scroll-behavior: auto !important;
transition-duration: .01ms !important;
}
}
document.querySelectorAll("[data-film-gallery]").forEach((gallery) => {
const stage = gallery.querySelector(".we-film__stage");
const track = gallery.querySelector("[data-film-track]");
const cards = Array.from(track.querySelectorAll(".we-film__card"));
const prev = gallery.querySelector("[data-film-prev]");
const next = gallery.querySelector("[data-film-next]");
const number = gallery.querySelector("[data-film-number]");
const total = gallery.querySelector("[data-film-total]");
const progress = gallery.querySelector("[data-film-progress]");
const dotsContainer = gallery.querySelector("[data-film-dots]");
if (!stage || !track || !cards.length) return;
let activeIndex = 0;
total.textContent = String(cards.length).padStart(2, "0");
dotsContainer.replaceChildren();
const dots = cards.map((card, index) => {
const dot = document.createElement("button");
dot.type = "button";
dot.setAttribute("aria-label", `Fotó ${index + 1}`);
dot.addEventListener("click", () => showCard(index));
dotsContainer.appendChild(dot);
return dot;
});
function showCard(index) {
activeIndex = (index + cards.length) % cards.length;
cards.forEach((card, i) => {
const active = i === activeIndex;
card.classList.toggle("is-active", active);
card.setAttribute("aria-current", active ? "true" : "false");
});
dots.forEach((dot, i) => {
const active = i === activeIndex;
dot.classList.toggle("is-active", active);
if (active) {
dot.setAttribute("aria-current", "true");
} else {
dot.removeAttribute("aria-current");
}
});
number.textContent = String(activeIndex + 1).padStart(2, "0");
progress.style.width =
`${((activeIndex + 1) / cards.length) * 100}%`;
const card = cards[activeIndex];
const centerOffset =
stage.clientWidth / 2 -
(card.offsetLeft + card.offsetWidth / 2);
track.style.transform = `translateX(${centerOffset}px)`;
}
prev.addEventListener("click", () => showCard(activeIndex - 1));
next.addEventListener("click", () => showCard(activeIndex + 1));
// A fotóra kattintva az adott kép kerül középre.
cards.forEach((card, index) => {
card.addEventListener("click", () => showCard(index));
});
// Billentyűzetes léptetés, ha a galéria kapja a fókuszt.
gallery.tabIndex = 0;
gallery.addEventListener("keydown", (event) => {
if (event.key === "ArrowLeft") {
event.preventDefault();
showCard(activeIndex - 1);
}
if (event.key === "ArrowRight") {
event.preventDefault();
showCard(activeIndex + 1);
}
});
// Újraközépre igazítás átméretezéskor.
if ("ResizeObserver" in window) {
const observer = new ResizeObserver(() => showCard(activeIndex));
observer.observe(stage);
} else {
window.addEventListener("resize", () => showCard(activeIndex));
}
showCard(0);
});