Project Draggable Card Slider dengan HTML, CSS, & JavaScript

Project Draggable Card Slider dengan HTML, CSS, & JavaScript

Cara Membuat Draggable Card Slider dengan HTML, CSS, & JavaScript: Panduan Lengkap dari Nol

Slider kartu (card slider) adalah komponen UI penting untuk menampilkan konten seperti testimonial, portofolio, atau produk. Di artikel ini, Anda akan belajar membuat draggable card slider dengan fitur:

  • Geser manual menggunakan mouse
  • Navigasi panah kiri/kanan
  • Scroll infinit tanpa batas
  • Autoplay otomatis
  • Responsif di semua ukuran layar

Berikut kode lengkap yang bisa langsung Anda implementasikan:

1. Struktur Folder & Persiapan

Buat folder dengan struktur berikut:

📂 draggable-slider/
├── 📄 index.html
├── 📄 style.css
├── 📄 script.js
└── 📂 images/ (6 gambar dengan nama img-1.jpg sampai img-6.jpg)

2. Kode HTML Lengkap

Salin seluruh kode ini ke index.html:

html
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Draggable Card Slider | Tutorial Lengkap</title>
    <link rel="stylesheet" href="style.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css">
</head>
<body>
    <div class="wrapper">
        <i id="left" class="fa-solid fa-angle-left"></i>
        <ul class="carousel">
            <li class="card">
                <div class="img"><img src="images/img-1.jpg" alt="Blanche Pearson" draggable="false"></div>
                <h2>Blanche Pearson</h2>
                <span>Sales Manager</span>
            </li>
            <li class="card">
                <div class="img"><img src="images/img-2.jpg" alt="Joenas Brauers" draggable="false"></div>
                <h2>Joenas Brauers</h2>
                <span>Web Developer</span>
            </li>
            <li class="card">
                <div class="img"><img src="images/img-3.jpg" alt="Lariach French" draggable="false"></div>
                <h2>Lariach French</h2>
                <span>Online Teacher</span>
            </li>
            <li class="card">
                <div class="img"><img src="images/img-4.jpg" alt="James Khosravi" draggable="false"></div>
                <h2>James Khosravi</h2>
                <span>Freelancer</span>
            </li>
            <li class="card">
                <div class="img"><img src="images/img-5.jpg" alt="Kristina Zasiadko" draggable="false"></div>
                <h2>Kristina Zasiadko</h2>
                <span>Bank Manager</span>
            </li>
            <li class="card">
                <div class="img"><img src="images/img-6.jpg" alt="Donald Horton" draggable="false"></div>
                <h2>Donald Horton</h2>
                <span>App Designer</span>
            </li>
        </ul>
        <i id="right" class="fa-solid fa-angle-right"></i>
    </div>
    <script src="script.js"></script>
</body>
</html>

3. Kode CSS Lengkap

Salin seluruh kode ini ke style.css:

css
/* Base Style */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: "Poppins", sans-serif;
}

body {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 0 35px;
    background: linear-gradient(to left top, #031A9A, #8B53FF);
}

/* Wrapper dan Navigasi */
.wrapper {
    max-width: 1100px;
    width: 100%;
    position: relative;
}

.wrapper i {
    height: 50px;
    width: 50px;
    background: #fff;
    text-align: center;
    line-height: 50px;
    border-radius: 50%;
    cursor: pointer;
    position: absolute;
    top: 50%;
    font-size: 1.25rem;
    transform: translateY(-50%);
    box-shadow: 0 3px 6px rgba(0,0,0,0.23);
    transition: transform 0.1s linear;
}

.wrapper i:active {
    transform: translateY(-50%) scale(0.85);
}

.wrapper i:first-child {
    left: -22px;
}

.wrapper i:last-child {
    right: -22px;
}

/* Grid System */
.carousel {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: calc((100% / 3) - 12px);
    gap: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;
}

.carousel::-webkit-scrollbar {
    display: none;
}

.carousel.dragging {
    scroll-snap-type: none;
    scroll-behavior: auto;
}

.carousel.dragging .card {
    cursor: grab;
    user-select: none;
}

.carousel .card {
    scroll-snap-align: start;
    height: 342px;
    list-style: none;
    background: #fff;
    border-radius: 8px;
    padding-bottom: 15px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}

.card .img {
    background: #8B53FF;
    width: 148px;
    height: 148px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

.card .img img {
    width: 140px;
    height: 140px;
    object-fit: cover;
    border-radius: 50%;
    border: 4px solid #fff;
}

.card h2 {
    font-weight: 500;
    font-size: 1.56rem;
    margin: 30px 0 5px;
}

.card span {
    color: #6A6D78;
    font-size: 1.31rem;
}

/* Responsive Design */
@media screen and (max-width: 900px) {
    .carousel {
        grid-auto-columns: calc((100% / 2) - 9px);
    }
}

@media screen and (max-width: 600px) {
    .carousel {
        grid-auto-columns: 100%;
    }
}

4. Kode JavaScript Lengkap

Salin seluruh kode ini ke script.js:

javascript
const wrapper = document.querySelector(".wrapper");
const carousel = document.querySelector(".carousel");
const firstCardWidth = carousel.querySelector(".card").offsetWidth;
const arrowBtns = document.querySelectorAll(".wrapper i");
const carouselChildrens = [...carousel.children];

let isDragging = false, 
    isAutoPlay = true, 
    startX, 
    startScrollLeft, 
    timeoutId;

// Hitung jumlah kartu yang tampil
const cardPerView = Math.round(carousel.offsetWidth / firstCardWidth);

// Duplikasi kartu untuk efek infinite
carouselChildrens.slice(-cardPerView).reverse().forEach(card => {
    carousel.insertAdjacentHTML("afterbegin", card.outerHTML);
});

carouselChildrens.slice(0, cardPerView).forEach(card => {
    carousel.insertAdjacentHTML("beforeend", card.outerHTML);
});

// Inisialisasi scroll position
carousel.classList.add("no-transition");
carousel.scrollLeft = carousel.offsetWidth;
carousel.classList.remove("no-transition");

// Event listener untuk tombol panah
arrowBtns.forEach(btn => {
    btn.addEventListener("click", () => {
        carousel.scrollLeft += btn.id === "left" ? -firstCardWidth : firstCardWidth;
    });
});

// Fungsi drag
const dragStart = (e) => {
    isDragging = true;
    carousel.classList.add("dragging");
    startX = e.pageX;
    startScrollLeft = carousel.scrollLeft;
}

const dragging = (e) => {
    if(!isDragging) return;
    carousel.scrollLeft = startScrollLeft - (e.pageX - startX);
}

const dragStop = () => {
    isDragging = false;
    carousel.classList.remove("dragging");
}

// Fungsi infinite scroll
const infiniteScroll = () => {
    if(carousel.scrollLeft === 0) {
        carousel.classList.add("no-transition");
        carousel.scrollLeft = carousel.scrollWidth - (2 * carousel.offsetWidth);
        carousel.classList.remove("no-transition");
    } else if(Math.ceil(carousel.scrollLeft) === carousel.scrollWidth - carousel.offsetWidth) {
        carousel.classList.add("no-transition");
        carousel.scrollLeft = carousel.offsetWidth;
        carousel.classList.remove("no-transition");
    }
}

// Autoplay
const autoPlay = () => {
    if(window.innerWidth < 800 || !isAutoPlay) return;
    timeoutId = setTimeout(() => carousel.scrollLeft += firstCardWidth, 2500);
}

autoPlay();

// Event listeners
carousel.addEventListener("mousedown", dragStart);
carousel.addEventListener("mousemove", dragging);
document.addEventListener("mouseup", dragStop);
carousel.addEventListener("scroll", infiniteScroll);
wrapper.addEventListener("mouseenter", () => clearTimeout(timeoutId));
wrapper.addEventListener("mouseleave", autoPlay);

5. Penjelasan Fitur Utama

  1. Infinite Scroll
    • Teknik duplikasi kartu di awal dan akhir carousel
    • Reset scroll position tanpa animasi saat mencapai ujung
  2. Drag & Drop
    • Hitung pergerakan mouse menggunakan pageX
    • Update scrollLeft berdasarkan perbedaan posisi
  3. Autoplay
    • Geser otomatis setiap 2.5 detik
    • Berhenti saat mouse berada di area slider
  4. Responsive Design
    • Grid layout berubah dari 3 → 2 → 1 kolom sesuai lebar layar
    • Media query untuk mobile-first approach

Kesimpulan dan Kata Penutup
Membuat draggable card slider dari nol adalah cara efektif untuk memahami konsep fundamental front-end development: manipulasi DOM dengan JavaScript, teknik CSS Grid, dan logika interaksi pengguna. Dengan menyelesaikan proyek ini, Anda telah mempraktikkan:

  • Manajemen Scroll: Infinite scroll tanpa library eksternal.
  • Drag Interaction: Geser elemen dengan event mouse.
  • Responsive Design: Adaptasi layout untuk mobile dan desktop.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *