웹솔루션개발 25년 노하우! 해피CGI의 모든것

[해피CGI][cgimall] Card Carousel -카드 슬라이더 본문

웹프로그램밍 자료실/HTML 자료

[해피CGI][cgimall] Card Carousel -카드 슬라이더

해피CGI윤실장 2025. 6. 24. 09:06

#카드 슬라이더 #회전형 카드 목록 #카드형 이미지 롤링 #카드 회전 뷰어

카드 형식의 콘텐츠를 좌우로 부드럽게 넘길 수 있도록 구성된 이 캐러셀은, 
반응형 디자인과 터치/마우스 인터랙션을 지원하여 다양한 환경에서 시각적으로 깔끔한 콘텐츠 탐색을 제공합니다.

HTML 구조

<h1 class="about-title">OUR TEAM</h1>

 

<div class="carousel-container">

<button class="nav-arrow left">‹</button>

<div class="carousel-track">

<div class="card" data-index="0">

Team Member 1

</div>

<div class="card" data-index="1">

Team Member 2

</div>

<div class="card" data-index="2">

Team Member 3

</div>

<div class="card" data-index="3">

Team Member 4

</div>

<div class="card" data-index="4">

Team Member 5

</div>

<div class="card" data-index="5">

Team Member 6

</div>

</div>

<button class="nav-arrow right">›</button>

</div>

 

<div class="member-info">

<h2 class="member-name">David Kim</h2>

<p class="member-role">Founder</p>

</div>

 

<div class="dots">

<div class="dot active" data-index="0"></div>

<div class="dot" data-index="1"></div>

<div class="dot" data-index="2"></div>

<div class="dot" data-index="3"></div>

<div class="dot" data-index="4"></div>

<div class="dot" data-index="5"></div>

</div>



CSS 소스

* {

margin: 0;

padding: 0;

box-sizing: border-box;

font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

}

 

body {

min-height: 100vh;

display: flex;

flex-direction: column;

align-items: center;

justify-content: center;

background-color: #f5f5f5;

overflow: hidden;

}

 

.about-title {

font-size: 7.5rem;

font-weight: 900;

text-transform: uppercase;

letter-spacing: -0.02em;

position: absolute;

top: 45px;

left: 50%;

transform: translateX(-50%);

pointer-events: none;

white-space: nowrap;

font-family: "Arial Black", "Arial Bold", Arial, sans-serif;

background: linear-gradient(

to bottom,

rgb(8 42 123 / 35%) 30%,

rgb(255 255 255 / 0%) 76%

);

-webkit-background-clip: text;

background-clip: text;

color: transparent;

}

 

.carousel-container {

width: 100%;

max-width: 1200px;

height: 450px;

position: relative;

perspective: 1000px;

margin-top: 80px;

}

 

.carousel-track {

width: 100%;

height: 100%;

display: flex;

justify-content: center;

align-items: center;

position: relative;

transform-style: preserve-3d;

transition: transform 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);

}

 

.card {

position: absolute;

width: 280px;

height: 380px;

background: white;

border-radius: 20px;

overflow: hidden;

box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);

transition: all 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);

cursor: pointer;

}

 

.card img {

width: 100%;

height: 100%;

object-fit: cover;

transition: all 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);

}

 

.card.center {

z-index: 10;

transform: scale(1.1) translateZ(0);

}

 

.card.center img {

filter: none;

}

 

.card.left-2 {

z-index: 1;

transform: translateX(-400px) scale(0.8) translateZ(-300px);

opacity: 0.7;

}

 

.card.left-2 img {

filter: grayscale(100%);

}

 

.card.left-1 {

z-index: 5;

transform: translateX(-200px) scale(0.9) translateZ(-100px);

opacity: 0.9;

}

 

.card.left-1 img {

filter: grayscale(100%);

}

 

.card.right-1 {

z-index: 5;

transform: translateX(200px) scale(0.9) translateZ(-100px);

opacity: 0.9;

}

 

.card.right-1 img {

filter: grayscale(100%);

}

 

.card.right-2 {

z-index: 1;

transform: translateX(400px) scale(0.8) translateZ(-300px);

opacity: 0.7;

}

 

.card.right-2 img {

filter: grayscale(100%);

}

 

.card.hidden {

opacity: 0;

pointer-events: none;

}

 

.member-info {

text-align: center;

margin-top: 40px;

transition: all 0.5s ease-out;

}

 

.member-name {

color: rgb(8, 42, 123);

font-size: 2.5rem;

font-weight: 700;

margin-bottom: 10px;

position: relative;

display: inline-block;

}

 

.member-name::before,

.member-name::after {

content: "";

position: absolute;

top: 100%;

width: 100px;

height: 2px;

background: rgb(8, 42, 123);

}

 

.member-name::before {

left: -120px;

}

 

.member-name::after {

right: -120px;

}

 

.member-role {

color: #848696;

font-size: 1.5rem;

font-weight: 500;

opacity: 0.8;

text-transform: uppercase;

letter-spacing: 0.1em;

padding: 10px 0;

margin-top: -15px;

position: relative;

}

.dots {

display: flex;

justify-content: center;

gap: 10px;

margin-top: 60px;

}

 

.dot {

width: 12px;

height: 12px;

border-radius: 50%;

background: rgba(8, 42, 123, 0.2);

cursor: pointer;

transition: all 0.3s ease;

}

 

.dot.active {

background: rgb(8, 42, 123);

transform: scale(1.2);

}

 

.nav-arrow {

position: absolute;

top: 50%;

transform: translateY(-50%);

background: rgba(8, 42, 123, 0.6);

color: white;

width: 40px;

height: 40px;

border-radius: 50%;

display: flex;

align-items: center;

justify-content: center;

cursor: pointer;

z-index: 20;

transition: all 0.3s ease;

font-size: 1.5rem;

border: none;

outline: none;

padding-bottom: 4px;

}

 

.nav-arrow:hover {

background: rgba(0, 0, 0, 0.8);

transform: translateY(-50%) scale(1.1);

}

 

.nav-arrow.left {

left: 20px;

padding-right: 3px;

}

 

.nav-arrow.right {

right: 20px;

padding-left: 3px;

}

 

@media (max-width: 768px) {

.about-title {

font-size: 4.5rem;

}

 

.card {

width: 200px;

height: 280px;

}

 

.card.left-2 {

transform: translateX(-250px) scale(0.8) translateZ(-300px);

}

 

.card.left-1 {

transform: translateX(-120px) scale(0.9) translateZ(-100px);

}

 

.card.right-1 {

transform: translateX(120px) scale(0.9) translateZ(-100px);

}

 

.card.right-2 {

transform: translateX(250px) scale(0.8) translateZ(-300px);

}

 

.member-name {

font-size: 2rem;

}

 

.member-role {

font-size: 1.2rem;

}

 

.member-name::before,

.member-name::after {

width: 50px;

}

 

.member-name::before {

left: -70px;

}

 

.member-name::after {

right: -70px;

}

}

 



JS 소스

const teamMembers = [

{ name: "Emily Kim", role: "Founder" },

{ name: "Michael Steward", role: "Creative Director" },

{ name: "Emma Rodriguez", role: "Lead Developer" },

{ name: "Julia Gimmel", role: "UX Designer" },

{ name: "Lisa Anderson", role: "Marketing Manager" },

{ name: "James Wilson", role: "Product Manager" }

];

 

const cards = document.querySelectorAll(".card");

const dots = document.querySelectorAll(".dot");

const memberName = document.querySelector(".member-name");

const memberRole = document.querySelector(".member-role");

const leftArrow = document.querySelector(".nav-arrow.left");

const rightArrow = document.querySelector(".nav-arrow.right");

let currentIndex = 0;

let isAnimating = false;

 

function updateCarousel(newIndex) {

if (isAnimating) return;

isAnimating = true;

 

currentIndex = (newIndex + cards.length) % cards.length;

 

cards.forEach((card, i) => {

const offset = (i - currentIndex + cards.length) % cards.length;

 

card.classList.remove(

"center",

"left-1",

"left-2",

"right-1",

"right-2",

"hidden"

);

 

if (offset === 0) {

card.classList.add("center");

} else if (offset === 1) {

card.classList.add("right-1");

} else if (offset === 2) {

card.classList.add("right-2");

} else if (offset === cards.length - 1) {

card.classList.add("left-1");

} else if (offset === cards.length - 2) {

card.classList.add("left-2");

} else {

card.classList.add("hidden");

}

});

 

dots.forEach((dot, i) => {

dot.classList.toggle("active", i === currentIndex);

});

 

memberName.style.opacity = "0";

memberRole.style.opacity = "0";

 

setTimeout(() => {

memberName.textContent = teamMembers[currentIndex].name;

memberRole.textContent = teamMembers[currentIndex].role;

memberName.style.opacity = "1";

memberRole.style.opacity = "1";

}, 300);

 

setTimeout(() => {

isAnimating = false;

}, 800);

}

 

leftArrow.addEventListener("click", () => {

updateCarousel(currentIndex - 1);

});

 

rightArrow.addEventListener("click", () => {

updateCarousel(currentIndex + 1);

});

 

dots.forEach((dot, i) => {

dot.addEventListener("click", () => {

updateCarousel(i);

});

});

 

cards.forEach((card, i) => {

card.addEventListener("click", () => {

updateCarousel(i);

});

});

 

document.addEventListener("keydown", (e) => {

if (e.key === "ArrowLeft") {

updateCarousel(currentIndex - 1);

} else if (e.key === "ArrowRight") {

updateCarousel(currentIndex + 1);

}

});

 

let touchStartX = 0;

let touchEndX = 0;

 

document.addEventListener("touchstart", (e) => {

touchStartX = e.changedTouches[0].screenX;

});

 

document.addEventListener("touchend", (e) => {

touchEndX = e.changedTouches[0].screenX;

handleSwipe();

});

 

function handleSwipe() {

const swipeThreshold = 50;

const diff = touchStartX - touchEndX;

 

if (Math.abs(diff) > swipeThreshold) {

if (diff > 0) {

updateCarousel(currentIndex + 1);

 

Comments