Draggable Card Carousel
A touch-friendly draggable carousel with momentum scrolling, cards that snap to center, smooth elastic boundaries, and 3D perspective tilt effects. Modern, sleek, and beautiful.
Build a highly interactive, touch-friendly draggable carousel with momentum scrolling and 3D perspective effects. This snippet uses vanilla JavaScript for physics-based movement and CSS perspective for depth.
HTML Structure
The structure consists of a container, a track that holds the cards, and a container for navigation indicators.
<div class="carousel-container">
<div class="carousel-track">
<!-- Card 1 -->
<div class="carousel-card">
<div class="card-visual visual-gradient-1"></div>
<div class="card-content">
<h3>Neon Dreams</h3>
<p>Experience the vibrant glow of the future.</p>
</div>
</div>
<!-- More Cards... -->
</div>
<div class="carousel-indicators">
<!-- Indicators injected by JS -->
</div>
</div>
CSS Styling
Key styling includes perspective on the container and transform-style: preserve-3d on the track. Cards are styled with glassmorphism using backdrop-filter.
:root {
--bg-color: #050505;
--card-bg: rgba(255, 255, 255, 0.05);
--gap: 40px;
--card-width: 320px;
--card-height: 480px;
}
.carousel-container {
width: 100%;
height: 100vh;
position: relative;
display: flex;
align-items: center;
perspective: 1000px; /* Essential for 3D depth */
}
.carousel-track {
display: flex;
gap: var(--gap);
cursor: grab;
align-items: center;
transform-style: preserve-3d;
will-change: transform;
}
.carousel-card {
width: var(--card-width);
height: var(--card-height);
flex-shrink: 0;
border-radius: 24px;
background: var(--card-bg);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
overflow: hidden;
transition: box-shadow 0.3s ease;
user-select: none;
}
JavaScript Implementation
The JavaScript handles:
- Drag & Momentum: Tracks mouse/touch movement and applies velocity with friction.
- Snap to Center: calculating the nearest card to stop at.
- 3D Tilt & Scale: Rotates and scales cards based on their distance from the center of the viewport.
class DraggableCarousel {
constructor(track, container) {
this.track = track;
this.container = container;
this.cards = Array.from(track.children);
this.state = {
currentX: 0,
targetX: 0,
isDragging: false,
startX: 0,
velocity: 0
};
this.init();
}
init() {
// Event listeners setup (start, move, end)
// ...
this.animate();
}
handleMove(e) {
if (!this.state.isDragging) return;
const x = this.getX(e);
const diff = x - this.state.startX;
this.state.currentX += diff;
this.state.targetX = this.state.currentX;
this.state.velocity = diff;
this.state.startX = x;
}
animate() {
// Smooth follow logic for drag
if (!this.state.isDragging) {
this.state.currentX += (this.state.targetX - this.state.currentX) * 0.1;
}
// Apply transform to track
this.track.style.transform = `translateX(${this.state.currentX}px)`;
// 3D Effect for each card
this.cards.forEach((card, i) => {
// Calculation of distFromCenter...
const rotateY = (distFromCenter / maxDist) * 45;
const scale = 1 - Math.abs(distFromCenter / maxDist) * 0.2;
card.style.transform = `perspective(1000px) rotateY(${rotateY}deg) scale(${scale})`;
});
requestAnimationFrame(this.animate.bind(this));
}
}