[ Javascript | CSS | TIL] 카드 뒤집기 효과

Haksoo JI·2022년 11월 16일
0

[ TIL ]

목록 보기
2/30

카드 뒤집기 효과

미니프로젝트로 팀소개 페이지를 만들게 되었다. 팀 전체를 소개하는 페이지와 각 팀원을 소개하는 페이지로 구성하기로 했는데, 개인페이지는 각자 개성을 담아 만들어보기로 했다. 어떤 식으로 만들까 고민하다가 카드 뒤집기 효과로 사진을 클릭하면 소개 글이 나오는 느낌으로 만들고 싶어졌다.

구글링을 해보니 CSS로 구현하는 것이 더 빠르고 가볍고 편하다고 했다. 그래서 그런지 Javascript로 구현하는 코드는 찾기가 힘들었다. Javascript를 활용한다고 해도 대부분 CSS로 만들어놓은 효과를 가지고 있는 클래스를 만들어서 classList를 활용해서 add, remove, toggle 메서드 등으로 추가하는 식이었다. 하지만 나는 최근 계속 Javascript를 공부하고 있다보니 공부한 내용을 실습할 겸 Javascript를 조금은 더 활용해서 구현해 보고 싶었다.

Node.js를 활용한 백엔드 개발자가 되기위한 공부를 하고있다보니 DOM을 활용하는 부분은 좀 가볍게 훑어 넘기며 공부한 편이었는데, 화면 구현을 위해 사용하려다보니 의도치 않게 실습을 하게 되었는데 상당히 재미있어서 좋았다. 확실히 Javascript는 매력덩어리 언어라는 생각이 또 들었다.

다음은 카드 뒤집기 효과를 추가한 것이다.

어떤 식으로 구현했는지 기록해보겠다.

HTML, CSS

기본 HTML 구성은 다음과 같이 했다.

<div class="card">  
  <div class="front">앞면</div>
  <div class="back">뒷면</div>
</div>

다음은 CSS 코드이다.

.card {
  width: 350px;
  height: 350px;
  perspective: 1100px;
  position: relative;
  margin: 10px;
  transition: 0.4s;
  transform-style: preserve-3d;
}

.front,
.back {
  position: absolute;
  width: 100%;
  height: 100%;
}

.front {
  background: teal;
}

.back {
  backface-visibility: hidden;
  background: white;
  transform: rotateY(180deg);
}

전체를 묶고있는 .card 블록에는 뒤집을 때 시각적 효과를 위해서 perspective: 1100px 속성으로 z축에 깊이를 주었고, transform-style: preserve-3d로는 3D 입체감을 주었다.

내부에 .front, .back 블록들은 position: absolute 속성으로 같은 위치에 존재하게 한 후, .backtransform: rotateY(180deg) 속성을 주어서 뒤집힌 상태로 놓이게 두었다. 그리고 그 때 앞면에 반전된 뒷 면의 글들이 투영되어 보이지 않도록 backface-visibility: hidden 속성을 준 것이다.

이제는 이렇게 만들어진 카드를 클릭하면 뒤집어지도록 만들면 된다.

Javascript

맨 처음에 앞면에서 뒷면으로 만들어주는 것은 쉬웠다.

const card = document.querySelector(".card")
  card.addEventListener("click", flipper)
  
  function flipper (event) {
    const target = event.currentTarget
    target.style.transform = "rotateY(180deg)"
  }

하지만 이 코드는 카드를 뒷면에서 앞면으로 다시 뒤집어주지는 못했다. 그래서 어떻게할까 고민하다가 flipper() 함수에 이벤트리스너를 추가시켰다.

function flipper (event) {
  const target = event.currentTarget
  target.style.transform = "rotateY(180deg)"
  target.addEventListener("click", innerFlipper)
}

function innerFlipper (event) {
  const target = event.currentTarget
  target.style.transform = "rotateY(0deg)"
}

이렇게 하면 앞면을 클릭하게 되면 뒷면으로 뒤집히고, 뒷면을 클릭했을 때 앞면으로 다시 돌려주는 이벤트리스너가 뒷면에 추가되어 해결될 것이라고 예상했다.
코드는 예상대로 작동했지만, 1회성으로만 작동했다. 앞에서 뒤로 1번, 뒤에서 앞으로 1번 뒤집어진 후에는 다시 클릭해도 더이상 움직이지 않았다. 어째서일까? 고민을 하던 중에 크롬개발자툴을 사용해서 요소를 검사해보았다.

앞뒷면 모두 한번씩 뒤집힌 후 앞면을 보였을 때, 더이상 움직이지 않는 상태의 요소를 검사한 화면이다. click 하위에 저장된 이벤트를 보여주고있다. 52번째 줄은 innerFlipper 였고 46번째 줄을 확인해보니 flipper 리스너였다.
innerFlipper가 flipper 보다 상위에 나타나고, 클릭시 움직이지 않는 것을 생각해봤을 때 현재 카드요소는 click 이벤트가 발생했을 때 앞->뒤 만드는 flipper가 아니라 뒤->앞으로 만드는 innerFlipper가 작동하는 것 같다.
(예상으로는 flipper에 의해 나중에 추가된 함수라 그런 것 같은데, 관련 내용이 있는지 찾아봐야겠다.)

하지만 현재 뒷면인 상태라서 이미 target.style.transform = "rotateY(180deg)" 속성을 가지고 있는데, 같은 이벤트를 또다시 받으면 카드는 움직이지 않는다. 그럼 어떻게 해야할까? 다시 고민이 시작되었다.

문제 해결을 위해 뒷면에서 다시 앞면으로 뒤집기 위해 현재 상태와 필요한 과정을 다시 정리해보자.

  • 현재상태: rotateY(0deg) / 가지고 있는 이벤트리스너: click => 우선적용: innerFlipper: rotateY(0deg), flipper: rotateY(180deg)
  • click 이벤트 발생시 innerFlipper가 아니라 flipper가 작동하도록 만들어야함.
    • innerFlipper 내부에 flipper를 추가하는 코드를 시도해봤으나 작동하지 않음.
    • 개발툴에서 보이는 이벤트리스너의 순서도 변화가 없음.
    • 같은 이벤트가 이미 존재하여서 순서가 초기화되지 않는 것 같다고 생각함.
    • flipper를 추가하는 코드 아래에 innerFlipper를 삭제하는 코드를 추가.
    • 정상적으로 작동된다.

완성

다음은 작동된 코드이다.

document.addEventListener('DOMContentLoaded', () => {

  const cards = document.querySelectorAll(".card")
  for(const card of cards) {
    card.addEventListener("click", flipper)
  }
  
    function flipper (event) {
      const target = event.currentTarget
      target.style.transform = "rotateY(180deg)"
      target.addEventListener("click", innerFlipper)
    }

    function innerFlipper (event) {
      const target = event.currentTarget
      target.style.transform = "rotateY(0deg)"
      target.addEventListener("click", flipper)
      target.removeEventListener("click", innerFlipper)
    }
})

자세한 소스코드는 여기에.

profile
아직 씨앗입니다. 무슨 나무가 될까요?

0개의 댓글