Portföy Resim Karoseli ve Senkronize Kaydırıcılar ile Web Sayfasında İnteraktif Bir Deneyim Oluşturma
Web sayfalarında görsel içeriği etkili bir şekilde sunmak, kullanıcı deneyimi açısından kritik öneme sahiptir. Özellikle bir portföyü sergilerken, ziyaretçilerin çalışmalarınızı kolayca keşfedebilmesi ve etkileşimde bulunabilmesi, projelerinizin değerini artırır. Bu bağlamda, senkronize kaydırıcılara sahip bir resim karoseli (carousel), standart bir galeriye kıyasla çok daha dinamik ve kullanıcı dostu bir çözüm sunar. Bu makalede, modern web teknolojilerini (HTML, CSS, JavaScript) kullanarak, büyük bir ana resim kaydırıcısı ile altında yer alan küçük resim (thumbnail) kaydırıcısının senkronize bir şekilde çalıştığı bir portföy karoseli nasıl oluşturulacağını adım adım inceleyeceğiz.
Giriş: Neden Senkronize Kaydırıcılar?
Bir portföy web sitesi, görsel odaklı bir içerik sunar. Ziyaretçilerinizin projelerinizi detaylı bir şekilde incelemesini sağlamak için, büyük ve dikkat çekici bir ana resim alanı ve bu resimler arasında kolayca geçiş yapmalarını sağlayacak bir navigasyon mekanizması gereklidir. Geleneksel navigasyon okları veya noktaları yeterli olsa da, küçük resimlerden oluşan bir alt kaydırıcı, kullanıcılara tüm görselleri bir bakışta görme ve doğrudan istedikleri görsele atlama imkanı sunar.
Senkronize kaydırıcılar, bu iki bileşenin birbiriyle uyum içinde çalışmasını sağlar. Ana kaydırıcıda bir resim değiştiğinde, alt kaydırıcıdaki ilgili küçük resim otomatik olarak vurgulanır ve hatta gerekirse kaydırılır. Aynı şekilde, alt kaydırıcıdaki bir küçük resme tıklandığında, ana kaydırıcı ilgili büyük resmi gösterir. Bu çift yönlü senkronizasyon, kullanıcıya sezgisel ve kesintisiz bir gezinme deneyimi sunar, böylece dikkat dağılmadan içeriğe odaklanabilirler. Bu yaklaşım, sadece estetik bir avantaj sağlamakla kalmaz, aynı zamanda erişilebilirliği ve genel kullanılabilirliği de önemli ölçüde artırır.
Temel Yapı Taşları: HTML, CSS ve JavaScript
Bu interaktif karoseli oluşturmak için üç temel web teknolojisini kullanacağız:
* HTML (HyperText Markup Language): Karoselin iskeletini, yani ana resimlerin, küçük resimlerin, navigasyon oklarının ve diğer yapısal elemanların yerleşimini tanımlamak için kullanılır.
* CSS (Cascading Style Sheets): Karoselin görsel görünümünü, boyutlarını, renklerini, geçiş efektlerini ve düzenini stilize etmek için kullanılır. flexbox veya grid gibi modern CSS özellikleri, duyarlı bir düzen oluşturmak için idealdir.
* JavaScript: Karoselin dinamik davranışını, yani resimler arasında kaydırma mantığını, navigasyon olaylarını, senkronizasyonu ve isteğe bağlı olarak otomatik oynatma işlevselliğini sağlamak için kullanılır.
Şimdi, bu bileşenleri adım adım nasıl entegre edeceğimizi inceleyelim.
HTML Yapısını Oluşturma
Karoselimiz için temel HTML yapısı, bir ana kapsayıcı içinde iki ayrı kaydırıcı (biri büyük resimler, diğeri küçük resimler için) ve navigasyon oklarını içerecektir.
Bu yapıda:
* .carousel-container: Tüm karoseli saran ana kapsayıcıdır.
* .main-slider-wrapper: Ana resim kaydırıcısını ve ona ait navigasyon butonlarını sarmalar.
* .main-slider: İçinde büyük resimlerin bulunduğu, yatay olarak kaydırılacak olan alandır. Her etiketi bir slaytı temsil eder.
* .nav-button: Ana kaydırıcı için “önceki” ve “sonraki” butonlarıdır.
* .thumbnail-slider-wrapper: Küçük resim kaydırıcısını ve ona ait navigasyon butonlarını sarmalar.
* .thumbnail-slider: İçinde küçük resimlerin bulunduğu, yatay olarak kaydırılacak olan alandır. Her etiketi bir küçük resmi temsil eder. active sınıfı, o anki aktif küçük resmi belirtmek için kullanılır.
* .thumb-nav-button: Küçük resim kaydırıcısı için “önceki” ve “sonraki” butonlarıdır (isteğe bağlı, küçük resimlerin de kaydırılabilir olması durumunda).
CSS ile Görselleştirme ve Temel Düzen
Şimdi, HTML yapımıza stil vererek karoselin görünümünü ve temel kaydırma mekanizmasını oluşturalım.
.carousel-container {
max-width: 900px;
margin: 40px auto;
font-family: Arial, sans-serif;
position: relative;
}
.main-slider-wrapper {
position: relative;
width: 100%;
overflow: hidden; / Sadece bir resmi göster, diğerlerini gizle /
border: 1px solid #ddd;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
margin-bottom: 15px;
}
.main-slider {
display: flex; / Resimleri yan yana diz /
width: 100%; / JavaScript ile güncellenecek /
transition: transform 0.5s ease-in-out; / Kaydırma animasyonu /
}
.main-slider img {
width: 100%;
height: 450px; / Sabit bir yükseklik verilebilir /
object-fit: cover; / Resmi kapsayıcıya sığdır /
flex-shrink: 0; / Resimlerin küçülmesini engelle /
}
.nav-button {
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: rgba(0, 0, 0, 0.5);
color: white;
border: none;
padding: 10px 15px;
cursor: pointer;
font-size: 24px;
z-index: 10;
transition: background-color 0.3s ease;
}
.nav-button:hover {
background-color: rgba(0, 0, 0, 0.7);
}
.prev-button {
left: 10px;
}
.next-button {
right: 10px;
}
.thumbnail-slider-wrapper {
position: relative;
width: 100%;
overflow: hidden; / Küçük resimlerin de kaydırılabilir olmasını sağla /
}
.thumbnail-slider {
display: flex;
gap: 10px; / Küçük resimler arasında boşluk /
padding: 5px 0;
transition: transform 0.3s ease-out; / Küçük resim kaydırma animasyonu /
}
.thumbnail-slider img {
width: 100px; / Küçük resim genişliği /
height: 75px; / Küçük resim yüksekliği /
object-fit: cover;
cursor: pointer;
border: 2px solid transparent;
transition: border-color 0.3s ease, transform 0.2s ease;
flex-shrink: 0;
}
.thumbnail-slider img:hover {
transform: scale(1.05);
}
.thumbnail-slider img.active {
border-color: #007bff; / Aktif küçük resmi vurgula /
box-shadow: 0 0 8px rgba(0, 123, 255, 0.6);
}
.thumb-nav-button {
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: rgba(0, 0, 0, 0.3);
color: white;
border: none;
padding: 5px 8px;
cursor: pointer;
font-size: 18px;
z-index: 5;
transition: background-color 0.3s ease;
}
.thumb-nav-button:hover {
background-color: rgba(0, 0, 0, 0.5);
}
.thumb-prev-button {
left: 0;
}
.thumb-next-button {
right: 0;
}
/ Duyarlı tasarım için medya sorguları /
@media (max-width: 768px) {
.main-slider img {
height: 300px;
}
.thumbnail-slider img {
width: 80px;
height: 60px;
}
.nav-button {
padding: 8px 12px;
font-size: 20px;
}
}
@media (max-width: 480px) {
.main-slider img {
height: 200px;
}
.thumbnail-slider img {
width: 60px;
height: 45px;
}
.nav-button {
padding: 5px 10px;
font-size: 18px;
}
.prev-button { left: 5px; }
.next-button { right: 5px; }
}
Bu CSS kodunda dikkat edilmesi gerekenler:
* overflow: hidden: Hem ana hem de küçük resim kaydırıcılarının kapsayıcılarında bu özellik, sadece o anki görüntülenebilir alanı gösterir ve kaydırılacak içeriği gizler.
* display: flex: Resimleri yatay olarak yan yana sıralamak için kullanılır.
* flex-shrink: 0: Resimlerin, kapsayıcıları içindeki boşluğa göre küçülmesini engeller, böylece her resim kendi tam genişliğini korur.
* transform: translateX(): JavaScript ile kaydırma animasyonunu sağlamak için kullanılacak ana CSS özelliğidir. transition özelliği, bu değişimi yumuşak bir animasyonla yapar.
* object-fit: cover: Resimlerin en boy oranını bozmadan kapsayıcılarını doldurmasını sağlar.
* .thumbnail.active: Aktif olan küçük resmin belirgin bir çerçeve veya gölge ile vurgulanmasını sağlar.
JavaScript ile Dinamik İşlevsellik
Karoselimizin beyni JavaScript olacaktır. Kaydırma mantığını, navigasyon olaylarını, senkronizasyonu ve isteğe bağlı otomatik oynatmayı bu bölümde yöneteceğiz.
document.addEventListener('DOMContentLoaded', () => {
// DOM elemanlarını seçme
const mainSlider = document.querySelector('.main-slider');
const mainImages = document.querySelectorAll('.main-slider img');
const prevButton = document.querySelector('.prev-button');
const nextButton = document.querySelector('.next-button');
const thumbnailSlider = document.querySelector('.thumbnail-slider');
const thumbnails = document.querySelectorAll('.thumbnail-slider img');
const thumbPrevButton = document.querySelector('.thumb-prev-button');
const thumbNextButton = document.querySelector('.thumb-next-button');
// Durum değişkenleri
let currentIndex = 0;
let mainSlideWidth = mainImages[0].clientWidth; // Ana resim genişliği
let thumbnailWidth = thumbnails[0].clientWidth + 10; // Küçük resim genişliği + gap
// Ana kaydırıcının genişliğini ayarla (tüm resimleri yan yana sığdıracak kadar)
mainSlider.style.width = ${mainImages.length * mainSlideWidth}px;
// Kaydırıcıları belirli bir indekse taşıyan fonksiyon
function goToSlide(index) {
// Geçerli indeksi kontrol et
if (index < 0) {
currentIndex = mainImages.length - 1;
} else if (index >= mainImages.length) {
currentIndex = 0;
} else {
currentIndex = index;
}
// Ana kaydırıcıyı güncelle
const mainOffset = -currentIndex * mainSlideWidth;
mainSlider.style.transform = translateX(${mainOffset}px);
// Küçük resimlerin aktif durumunu güncelle
thumbnails.forEach((thumb, i) => {
if (i === currentIndex) {
thumb.classList.add('active');
} else {
thumb.classList.remove('active');
}
});
// Küçük resim kaydırıcısını senkronize et (aktif küçük resmi görünürde tut)
const thumbnailSliderWrapper = document.querySelector('.thumbnail-slider-wrapper');
const wrapperWidth = thumbnailSliderWrapper.clientWidth;
const visibleThumbnails = Math.floor(wrapperWidth / thumbnailWidth);
// Aktif thumbnail'ın başlangıç pozisyonu
const activeThumbPosition = currentIndex * thumbnailWidth;
// Thumbnail slider'ın kaydırması gereken maksimum sol pozisyon
const maxScrollLeft = (thumbnails.length * thumbnailWidth) - wrapperWidth;
let thumbOffset = 0;
if (activeThumbPosition + thumbnailWidth > wrapperWidth) {
// Aktif thumbnail sağa doğru kaydırılması gerekiyorsa
thumbOffset = -(activeThumbPosition - (wrapperWidth - thumbnailWidth));
if (thumbOffset > 0) thumbOffset = 0; // Başa dönme durumunu engelle
}
// Eğer aktif thumbnail çok soldaysa, slider'ı sola doğru kaydır
if (activeThumbPosition < -parseInt(thumbnailSlider.style.transform.replace('translateX(', '').replace('px)', '') || 0)) {
thumbOffset = -activeThumbPosition;
}
// Offset'i sınırlar içinde tut
if (thumbOffset > 0) thumbOffset = 0;
if (thumbOffset < -maxScrollLeft) thumbOffset = -maxScrollLeft;
thumbnailSlider.style.transform = translateX(${thumbOffset}px);
}
// Navigasyon butonları olay dinleyicileri
prevButton.addEventListener('click', () => {
goToSlide(currentIndex - 1);
});
nextButton.addEventListener('click', () => {
goToSlide(currentIndex + 1);
});
// Küçük resimlere tıklama olay dinleyicileri
thumbnails.forEach((thumb, index) => {
thumb.addEventListener('click', () => {
goToSlide(index);
});
});
// Küçük resim kaydırıcısı navigasyon butonları (isteğe bağlı)
thumbPrevButton.addEventListener('click', () => {
const thumbnailSliderWrapper = document.querySelector('.thumbnail-slider-wrapper');
const wrapperWidth = thumbnailSliderWrapper.clientWidth;
const currentThumbOffset = parseInt(thumbnailSlider.style.transform.replace('translateX(', '').replace('px)', '') || 0);
let newOffset = currentThumbOffset + wrapperWidth; // Bir ekran genişliği kadar geri kaydır
if (newOffset > 0) newOffset = 0; // Başa dönme sınırı
thumbnailSlider.style.transform = translateX(${newOffset}px);
});
thumbNextButton.addEventListener('click', () => {
const thumbnailSliderWrapper = document.querySelector('.thumbnail-slider-wrapper');
const wrapperWidth = thumbnailSliderWrapper.clientWidth;
const maxScrollLeft = (thumbnails.length * thumbnailWidth) - wrapperWidth;
const currentThumbOffset = parseInt(thumbnailSlider.style.transform.replace('translateX(', '').replace('px)', '') || 0);
let newOffset = currentThumbOffset - wrapperWidth; // Bir ekran genişliği kadar ileri kaydır
if (newOffset < -maxScrollLeft) newOffset = -maxScrollLeft; // Sona ulaşma sınırı
thumbnailSlider.style.transform = translateX(${newOffset}px);
});
// Otomatik kaydırma (isteğe bağlı)
let autoSlideInterval;
const startAutoSlide = () => {
autoSlideInterval = setInterval(() => {
goToSlide(currentIndex + 1);
}, 5000); // Her 5 saniyede bir kaydır
};
const stopAutoSlide = () => {
clearInterval(autoSlideInterval);
};
// Fare ana kaydırıcının üzerindeyken otomatik kaydırmayı durdur
mainSlider.parentElement.addEventListener('mouseenter', stopAutoSlide);
mainSlider.parentElement.addEventListener('mouseleave', startAutoSlide);
// İlk yüklemede ve pencere boyutu değiştiğinde genişlikleri yeniden hesapla
const updateDimensions = () => {
mainSlideWidth = mainImages[0].clientWidth;
thumbnailWidth = thumbnails[0].clientWidth + 10; // + gap
mainSlider.style.width = ${mainImages.length * mainSlideWidth}px;
goToSlide(currentIndex); // Güncel slayta tekrar git, pozisyonu ayarla
};
window.addEventListener('resize', updateDimensions);
// Karoseli başlat
goToSlide(0); // İlk resmi göster
startAutoSlide(); // Otomatik kaydırmayı başlat
});
#### JavaScript Kodunun Açıklaması:
1. DOM Elemanlarını Seçme: document.querySelector ve document.querySelectorAll kullanarak HTML elemanlarımızı JavaScript değişkenlerine atıyoruz. Bu, onlara daha kolay erişmemizi sağlar.
2. Durum Değişkenleri:
* currentIndex: Hangi resmin şu anda gösterildiğini takip eden bir sayıdır (0’dan başlar).
* mainSlideWidth: Ana resimlerin genişliğini saklar. Bu değer, transform: translateX() hesaplamalarında kullanılır.
* thumbnailWidth: Küçük resimlerin genişliğini ve aralarındaki boşluğu (gap) saklar. Küçük resim kaydırıcısının senkronizasyonu için gereklidir.
3. goToSlide(index) Fonksiyonu:
* Bu, karoselin kalbidir. Belirtilen index‘e göre ana ve küçük resim kaydırıcılarını günceller.
* İndeks Kontrolü: index‘in geçerli sınırlar içinde olduğundan emin olur (döngüsel kaydırma için).
* Ana Kaydırıcı Güncelleme: transform: translateX() kullanarak mainSlider‘ı currentIndex‘e göre kaydırır. mainOffset, kaydırma miktarını belirler.
* Küçük Resim Aktif Durumu: Tüm küçük resimlerin active sınıfını kaldırır ve sadece geçerli currentIndex‘e sahip küçük resme active sınıfını ekler.
* Küçük Resim Kaydırıcısını Senkronize Etme: Bu kısım, aktif küçük resmin her zaman görünür alanda olmasını sağlamak için karmaşık bir mantık içerir. thumbnailSlider‘ın ne kadar kaydırılması gerektiğini hesaplar ve transform: translateX() ile uygular. Bu, aktif küçük resim görünüm alanının dışına çıktığında kaydırıcının otomatik olarak ileri veya geri hareket etmesini sağlar.
4. Navigasyon Butonları: prevButton ve nextButton için click olay dinleyicileri eklenir. Tıklandığında, goToSlide fonksiyonunu çağırarak currentIndex‘i bir artırır veya azaltır.
5. Küçük Resim Tıklamaları: Her bir küçük resme click olay dinleyicisi eklenir. Bir küçük resme tıklandığında, o küçük resmin indeksi goToSlide fonksiyonuna iletilir, böylece ana kaydırıcı doğrudan o resme atlar.
6. Küçük Resim Kaydırıcısı Navigasyon Butonları (İsteğe Bağlı): thumbPrevButton ve thumbNextButton için de olay dinleyicileri eklenir. Bunlar, küçük resim kaydırıcısını bir ekran genişliği kadar ileri veya geri kaydırır.
7. Otomatik Kaydırma (İsteğe Bağlı):
* startAutoSlide(): setInterval kullanarak belirli aralıklarla goToSlide(currentIndex + 1) çağırır.
* stopAutoSlide(): clearInterval kullanarak otomatik kaydırmayı durdurur.
* Kullanıcı ana kaydırıcının üzerine geldiğinde otomatik kaydırmayı durdurur ve fare çıktığında tekrar başlatır.
8. Duyarlı Tasarım İçin Yeniden Boyutlandırma: updateDimensions() fonksiyonu, pencere boyutu değiştiğinde mainSlideWidth ve thumbnailWidth değerlerini yeniden hesaplar. Bu, karoselin farklı ekran boyutlarında doğru çalışmasını sağlar. window.addEventListener('resize', updateDimensions) ile bu fonksiyon pencere boyutu değiştiğinde otomatik olarak çağrılır.
9. Başlatma: DOMContentLoaded olay dinleyicisi içinde, sayfa yüklendiğinde goToSlide(0) çağrılarak ilk resim gösterilir ve startAutoSlide() ile otomatik kaydırma başlatılır.
Gelişmiş Özellikler ve İyileştirmeler
Temel karoselimiz artık çalışıyor. Ancak, kullanıcı deneyimini daha da zenginleştirmek ve erişilebilirliği artırmak için bazı gelişmiş özellikler ekleyebiliriz:
* Dokunmatik Kaydırma (Swipe Gestures): Mobil cihazlarda kullanıcıların parmaklarını sürükleyerek kaydırma yapmasını sağlamak için touchstart, touchmove ve touchend olay dinleyicilerini kullanarak kaydırma hareketlerini algılayabiliriz. Bu, özellikle dokunmatik ekranlı cihazlarda kullanıcı deneyimini büyük ölçüde iyileştirir.
* Klavye Navigasyonu: Klavyedeki sol ve sağ ok tuşlarını kullanarak kaydırma imkanı sunmak, erişilebilirliği artırır. keydown olay dinleyicisi ile bu tuşları yakalayabiliriz.
* Performans Optimizasyonları (Lazy Loading): Çok sayıda resim içeren bir portföyde, tüm resimleri sayfa yüklenirken yüklemek performansı düşürebilir. loading="lazy" özelliğini etiketlerine ekleyerek veya JavaScript ile özel bir “tembel yükleme” mekanizması uygulayarak sadece görünürdeki ve yakındaki resimlerin yüklenmesini sağlayabiliriz.
* Erişilebilirlik (ARIA Attributes): Ekran okuyucuları kullanan kişilerin karoseli anlamasını sağlamak için role="region", aria-live="polite", aria-label gibi ARIA nitelikleri eklenebilir. Navigasyon butonları için aria-label="Önceki resim" gibi açıklamalar eklemek de önemlidir.
* Farklı Geçiş Efektleri: transform: translateX() yerine opacity ve z-index kullanarak solma (fade) efektleri veya daha karmaşık 3D dönüşümler ile farklı görsel geçişler oluşturulabilir.
* Kütüphane Kullanımı: Bu makalede “Vanilla JavaScript” (yani herhangi bir kütüphane kullanmadan) ile nasıl yapılacağını gösterdik. Ancak, daha karmaşık karosel ihtiyaçlarınız varsa veya geliştirme süresini kısaltmak isterseniz, Swiper.js, Slick Carousel veya Owl Carousel gibi popüler JavaScript kütüphanelerini kullanabilirsiniz. Bu kütüphaneler genellikle dokunmatik kaydırma, lazy loading, döngüsel kaydırma gibi birçok özelliği kutudan çıkar çıkmaz sunar. Ancak, bu makalenin amacı temel mekaniği anlamaktı.
Sonuç
Bir portföy web sitesi için senkronize kaydırıcılara sahip interaktif bir resim karoseli oluşturmak, ziyaretçilerinize projelerinizi sunmanın oldukça etkili ve modern bir yoludur. HTML ile sağlam bir yapı oluşturarak, CSS ile göz alıcı bir görünüm kazandırarak ve JavaScript ile akıcı bir kullanıcı deneyimi sağlayarak, sadece görselleri sergilemekle kalmaz, aynı zamanda kullanıcıların içeriğinizle daha derinlemesine etkileşim kurmasını teşvik edersiniz.
Bu makalede adım adım sunduğumuz rehber, temel bir karosel oluşturmak için gerekli tüm adımları ve mantığı kapsamaktadır. Bu temel üzerine inşa ederek, dokunmatik kaydırma, klavye navigasyonu ve performans optimizasyonları gibi gelişmiş özelliklerle karoselinizi daha da zenginleştirebilirsiniz. Unutmayın, iyi tasarlanmış bir görsel sunum, portföyünüzün algılanan değerini artırır ve potansiyel müşteriler veya işverenler üzerinde kalıcı bir izlenim bırakır. Kendi yaratıcı dokunuşlarınızı ekleyerek bu yapıyı daha da kişiselleştirmekten çekinmeyin!
