CSS 애니메이션과 DOM 제어로 만드는 카드 셔플과 정렬 효과 #1

오늘은 CSS 애니메이션 효과를 이용하여 카드 셔플(Shuffle)과 카드 정렬 애니메이션 구현 방법을 자세하게 살펴보며, 자바스크립트와 HTML, CSS가 서로 어떻게 상호 작동하여 이런 동적인 기능을 만들어 내는지 정리해 보려 합니다.

CSS 애니메이션과 DOM 제어로 만드는 카드 셔플과 정렬 효과
CSS 애니메이션과 DOM 제어로 만드는 카드 셔플과 정렬 효과


CSS 애니메이션과 카드 셔플 효과

어떤 웹사이트를 방문해 보면, 단순히 텍스트와 이미지만 보여주는 것이 아니라 CSS 애니메이션 기능을 이용하여 다양한 움직임과 상호작용이 접목되어 있는 경우를 보여주기도 합니다. 그중에서도 카드 형태의 UI 요소는 게시판, 쇼핑몰 상품 목록, 포트폴리오, 이미지 갤러리 등 여러 곳에서 활용되는 대표적인 인터페이스입니다. 카드를 셔플(섞기)하거나 정렬하는 애니메이션은 인터랙션을 극대화하면서도 시각적으로 재미를 줄 수 있어, 사용자 경험(UX)을 한층 높이는 데 도움을 줄 수 있죠.


자바스크립트, HTML, CSS의 상관관계

이들의 관계를 간단하게 말하자면, HTML이 특정 웹 페이지 구조를 정의한다면, CSS가 디자인과 시각 효과를 담당하고, 자바스크립트가 이벤트 로직과 데이터를 다룹니다.
하나만으로는 완전한 애니메이션을 구현하기가 어렵지만, 이러한 3가지 기술이 유기적으로 연결되어 자연스러운 인터랙션을 만들어내는 거죠.

카드 셔플 에니메이션의 예로 이 3가지 기술의 상호 관계를 정리해 보면 다음과 같습니다.

  • HTML: 웹 페이지의 골격을 담당합니다. <div>나 <section>같은 태그를 이용해 구조를 잡고, 여기서 카드를 구성할 수 있는 영역을 만들어 둡니다.
  • CSS: HTML로 만든 골격에 디자인을 부여하는 역할을 합니다. 예컨대 카드에 그림자나 테두리를 주고, 정렬 방식이나 너비·높이를 설정해서 보기 좋게 배치합니다. 또, CSS 애니메이션을 구현할 때 transition, transform, animation 속성을 활용해 움직임과 시각적 효과를 더합니다.
  • 자바스크립트: 동적인 기능을 실현합니다. 단순히 정적인 웹페이지가 아닌, 버튼을 눌렀을 때 카드를 셔플하거나 정렬하는 등의 이벤트를 처리합니다. JavaScript로 DOM(Document Object Model)을 조작해 HTML 요소에 변화를 주고, CSS 클래스를 추가하거나 제거함으로써 움직임을 유도할 수 있습니다.


카드 셔플 애니메이션 구현 방법

먼저, CSS 애니메이션을 구현하기 전에 ‘어떻게 카드를 섞을 것인가?’라는 로직을 명확히 해야 합니다. 자바스크립트에서 가장 흔히 사용되는 방식 중 하나가 피셔–예이츠(Fisher–Yates) 알고리즘입니다. 이 알고리즘은 배열에 담긴 데이터를 무작위로 섞어 주는 데 탁월합니다.

카드 셔플은 크게 두 단계를 거칩니다.

데이터 섞기: 실제로 카드 정보를 담고 있는 배열을 무작위로 섞습니다.
화면에 반영: 섞인 순서대로 HTML 요소(카드 DOM 요소)를 재배치하고, CSS 애니메이션을 통해 시각적 효과를 줍니다.



구성 예시 코드

카드 셔플을 구현하기 위해 HTML, CSS 애니메이션, 자바스크립트 세 영역을 어떻게 구성하는지 간단한 예시를 살펴보겠습니다.

(1) HTML 구조

<!-- HTML -->
<div id="card-container">
  <div class="card" data-card-id="1">카드1</div>
  <div class="card" data-card-id="2">카드2</div>
  <div class="card" data-card-id="3">카드3</div>
  <div class="card" data-card-id="4">카드4</div>
  <div class="card" data-card-id="5">카드5</div>
</div>

<button id="shuffle-button">셔플</button>
  • card-container 내부에 여러 장의 카드를 배치했습니다.
  • 각 카드는 class=”card”를 공통으로 사용하며, 구분을 위해 data-card-id 속성을 붙였습니다.
  • 카드 섞기 동작을 유발할 수 있도록 #shuffle-button 버튼을 하나 두었습니다.

(2) CSS 스타일

/* CSS */
#card-container {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

.card {
  width: 80px;
  height: 120px;
  background-color: lightblue;
  border: 1px solid #666;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.6s ease; /* 카드가 이동할 때 부드럽게 움직이도록 */
}

/* 셔플될 때, 위치가 임의로 바뀌었다가 다시 돌아오는 애니메이션 예시 */
.card.shuffling {
  transform: rotate(720deg) translateX(100px);
}
  • card-container에는 여러 카드를 가로 방향으로 나란히 배치하도록 display: flex를 사용했습니다.
  • .card 요소는 기본 크기, 배경색, 테두리 등을 설정했으며, CSS 애니메이션 효과를 위해 transition 속성을 넣어 두었습니다.
  • 셔플 시점에 .shuffling 클래스를 임시로 적용하면 transform 속성이 활성화되어 회전과 이동 효과를 낼 수 있습니다.

(3) 자바스크립트 로직

// 자바스크립트

// 1) 카드 데이터를 배열로 구성
let cardData = [
  { id: 1, text: "카드1" },
  { id: 2, text: "카드2" },
  { id: 3, text: "카드3" },
  { id: 4, text: "카드4" },
  { id: 5, text: "카드5" }
];

// 2) Fisher–Yates 알고리즘을 이용한 셔플 함수
function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const randomIndex = Math.floor(Math.random() * (i + 1));
    [array[i], array[randomIndex]] = [array[randomIndex], array[i]];
  }
  return array;
}

// 3) 화면에 있는 카드 요소를 실제로 재배치하는 함수
function renderShuffledCards(shuffledData) {
  const container = document.getElementById("card-container");
  container.innerHTML = ""; // 기존 카드 초기화

  shuffledData.forEach((cardItem) => {
    // 새로운 순서대로 다시 DOM 요소를 생성
    const cardEl = document.createElement("div");
    cardEl.className = "card";
    cardEl.textContent = cardItem.text;
    cardEl.dataset.cardId = cardItem.id;
    
    container.appendChild(cardEl);
  });
}

// 4) 셔플 버튼 클릭 시 동작
document.getElementById("shuffle-button").addEventListener("click", () => {
  // (a) 먼저 카드에 shuffling 클래스를 추가하여 애니메이션 효과를 짧게 보여줌
  const allCards = document.querySelectorAll(".card");
  allCards.forEach((card) => {
    card.classList.add("shuffling");
  });
  
  // (b) 특정 시간(0.6초) 후 실제 배열을 섞고, 재배치한다
  setTimeout(() => {
    // 1. 데이터 셔플
    const shuffledData = shuffleArray(cardData);
    // 2. 셔플된 순서대로 화면 재배치
    renderShuffledCards(shuffledData);
  }, 600);
});

위 코드의 로직을 살펴보면 다음과 같은 순서로 진행됩니다.

  1. 버튼 클릭: 사용자가 “셔플” 버튼을 클릭합니다.
  2. 임시 애니메이션 적용: 기존에 존재하던 카드들에 .shuffling 클래스를 부여하여, 예를 들어 회전·이동되는 효과를 잠깐 보여줍니다.
  3. 데이터 섞기: 정해진 타이머(setTimeout) 후 카드 배열(cardData)을 무작위로 섞습니다.
  4. 화면에 반영: 새로운 순서대로 renderShuffledCards() 함수가 DOM 요소를 재구성하여, 섞인 결과를 시각적으로 보여줍니다.


단계별 동작 흐름 설명
  1. 데이터와 DOM의 분리
    • 카드를 단순히 화면 요소로만 다루지 말고, 자바스크립트 배열인 cardData로 논리적으로 저장하고 관리합니다.
    • 이처럼 ‘화면에 출력된 모습’과 ‘실제 데이터’를 분리하면 다양한 애니메이션이나 정렬, 필터링 기능을 확장하기가 훨씬 쉽습니다.
  2. 셔플 애니메이션 시점
    • 사용자가 버튼을 누르는 순간부터 카드를 “조금 흔들었다가” 실제로 자리를 바꾸는 애니메이션을 줄 수도 있고, 바로 결과 화면을 갱신할 수도 있습니다.
    • 예시 코드는 .shuffling 클래스를 잠시 적용했다가, 실제로 배열을 섞은 뒤에 새로운 DOM을 그리는 방식을 택했습니다.
    • 필요에 따라 .shuffling 클래스의 transform 속성을 다양하게 바꿔 보며 더욱 역동적인 느낌을 줄 수도 있습니다.
  3. 트랜지션과 타이밍
    • .card에 transition: transform 0.6s ease;가 설정되어 있으므로, transform 속성이 변경되면 0.6초 동안 부드럽게 변화가 일어납니다.
    • 셔플된 결과를 한꺼번에 재배치할 때도, 삽입·삭제를 자바스크립트로 처리하기 전에 잠깐의 지연 시간을 주거나 requestAnimationFrame을 활용해 애니메이션 프레임을 제어할 수 있습니다.
  4. 사용자 경험(UX) 고려
    • 카드가 번쩍 사라졌다가 나타나기보다는, 기존 위치에서 점진적으로 움직이거나 회전하는 애니메이션을 주면 시각적인 즐거움과 몰입도가 높아집니다.
    • 카드가 많은 경우 한꺼번에 움직이면 혼란스러울 수 있으므로, setTimeout 또는 requestAnimationFrame을 이용해 각 카드마다 약간의 간격을 두고 애니메이션을 적용하는 방법도 고려해 볼 수 있습니다.




OpenAI API를 활용한 새해 운세 프로그램 #1

답글 남기기