CSS 3D

2024. 5. 8. 15:12·Web
목차
  1. 개요.
  2. 3D 카드 뒤집기. 
  3. 애니메이션 만들기.
  4. 문제 해결하기 
  5. iOS 
  6. IE
  7. 전체 코드 
반응형

개요.

카드를 웹사이트에서 구현한다고 했을 때 뒤집는 애니메이션이 필요한 경우가 있다. 

그냥 앞뒤가 변화는 것보다 3D로 뒤집히는 것이 사용자가 봤을 때 자연스럽고 완성된 요소라고 생각할 수 있다. 

뒤집는 것을 생각할 때 보통 해결 하는 방법은 transform의 rotate를 사용하는 것이다. 

하지만 일반적으로 rotate를 사용해서 45도를 바꾸면 아래와 같이 나오게 된다. 

그냥 네모 박스가 쪼그라든 것 같은 형식이다. 

이 이유는 노란색 박스 즉, 카드를 포함하고 있는 부모 요소가 3차원 공간이어야 내부 요소도 3D로 동작한다. 

 

3D 카드 뒤집기. 

<body>
    <div class="world">
      <div class="card">CARD</div>
    </div>
</body>

 

먼저 부모 요소와 자식 카드 박스를 만들어준다. 

부모 박스가 위 이미지의 노란색 영역이고 카드는 붉은색 카드를 나타낸다. 

.world {
    display: flex;
    align-items: center;
    justify-content: center;

    width: 80vw;
    height: 80vh;

    background: yellow;
}
.card {
    display: flex;
    align-items: center;
    justify-content: center;

    width: 100px;
    height: 150px;

    margin: 1em;
    border-radius: 0.5rem;
    background: red;

    font-size: 1.5rem;
    color: white;
}

 

각각의 CSS 스타일! 

 

막간으로 rem은 html 전체의 px을 기준으로 나타나는 값이고 em은 해당 요소의 폰트 크기를 기준으로 나타낸다. 

html의 기본 폰트 크기가 20px이라고 했을 때 1rem은 20px 그리고 1rem을 가지고 있는 요소의 0.5em은 10px이다. 

이것은 폰트의 크기에 따라 유동적으로 값을 주기 위해서 사용한다. 

 

필수는 아니고 디자인에 따라서 사용하고 말고를 정하면 된다. 

.card {
    // ...
    transform: rotateY(45deg);
}

 

이제 뒤집는 효과를 위해서 카드에게 rotate를 45도 주었다. 

하지만 결과는 이미지에서 본 것과 똑같이 쪼그라든 붉은색 네모이다. 

 

이것을 3D로 만들기 위해서 부모 요소를 3차원으로 변경해줘야 한다. 

.world {
    // ...
    perspective: 500px;
}

 

새롭게 등장하는 perspective 속성이다.

이것은 사용자의 시각에서 값으로 주는 부분만큼의 원근감을 나타낸다.

실제로도 우리가 사물을 볼 때 가까이에 있는 것과 멀리 있는것의 원근감은 다르기 때문에 큰 값을 준다면

더 멀리 있다고 판단이 되므로 효과가 작게 느껴질 것이다. 

 

정리하면 수치가 작으면 3D 효과가 극적으로 나타나고, 클 수록 완만하게 나타난다.  

 

적용된 것을 확인하면 제대로 적용되어 있다. 하지만 3개의 카드를 넣었을 때 각각이 다른 회전 비율을 가지고 있는

것을 알 수 있는데, 위치에 따라 원근감이 달라져서 다르게 나타나는 것이다. 

 

이렇게 나타나는 이유는 보이지는 않지만 원근감의 변화가 소실점을 기준으로 변경되기 때문이다. 

기본 값은 center로 눈에 보이지 않지만 가운데에 소실점이 존재한다. 

 

perspective-origin 속성을 사용하면 해당 소실점의 위치를 변경할 수 있다. 

 

이것을 쉽게 해결하기 위해서는 world에서 있던 perspective 속성을 각각의 카드에게 제공 해줘야 한다.

그렇게 되면 카드마다의 소실점이 생기기 때문에 각도의 차이가 없어진다.  

.card {
    // ...

    transform: perspective(500px) rotateY(45deg);
}

 

world에 있던 perspective 속성을 제거하고 card의 transform에서 동일한 속성의 함수로 제공해 주었다. 

 

이제 결과물을 보면 모든 카드가 동일하게 회전하고 있는 것을 볼 수 있다. 

 

애니메이션 만들기.

이제 마우스를 올리면 카드가 뒤집어지는 애니메이션을 한번 만들어보자

뒤집는 애니메이션을 별로 어렵지 않다. 마우스 호버 했을 때 rotateY는 주면 그만! 

<!DOCTYPE html>
<html lang="ko">
  <head>
    <style>
      // ...
      
      .card {
        // ...

        transition: 1s;
        transform: rotateY(0);
      }

      .world:hover .card {
        transform: rotateY(180deg);
      }
    </style>
  </head>
  <body>
    <div class="world">
      <div class="card">CARD</div>
    </div>
  </body>
</html>

 

노란색 영역에 마우스가 있으면 카드가 뒤집히는 애니메이션이 만들어졌다. 매끄러운 애니메이션을 위해서 

transition으로 1s를 설정해주었다. 

 

이때 transform: rotateY(0)은 설정하지 않아도 애니메이션에는 문제없지만 초기 세팅을 해준다면 실제 애니메이션이

발생했을 때 성능상 이점이 있다고 한다. 

 

완성한 애니메이션을 보면 뒤집혔을 때 CARD가 뒤집혀서 나오는 것을 볼 수 있다. 

하지만 일반적인 카드는 뒤집히면 뒷면이 따로 존재하고 있다. 이것을 동일하게 만들어보자. 

 

먼저 베이스 코드를 조금 수정해줘야 하는데 :

  <body>
    <div class="world">
      <div class="card">
        <div class="card-side card-side-front">F</div>
        <div class="card-side card-side-back">B</div>
      </div>
    </div>
  </body>

 

앞면을 card-side-front 클래스를 통해서 나타내고 뒷면을 card-side-back을 통해서 만들어줬다. 

일반적으로는 F 부분이 나타나고 호버를 했을 땐 B가 출력되는 것을 목표로 만들 계획이다. 

 

일단 기본적인 애니메이션과 스타일을 주었다 :

  .card {
    position: relative;

    width: 100px;
    height: 150px;

    margin: 1em;

    transition: 1s;
    transform: rotateY(0);
  }

  .world:hover .card {
    transform: rotateY(180deg);
  }

  .card-side {
    position: absolute;
    top: 0;
    left: 0;

    width: 100%;
    height: 100%;

    display: flex;
    align-items: center;
    justify-content: center;

    border-radius: 0.5rem;

    font-size: 1.5rem;
    color: white;
  }

  .card-side-front {
    z-index: 1;

    background: red;
  }

  .card-side-back {
    background: blue;
  }

 

폰트라던지 정렬 등은 card-side에게 넘겨주고 앞면은 붉은색, 뒷면은 파란색으로 설정해 주었다. 

그리고 앞면과 뒷면이 겹쳐야하기 때문에 card에게 position: relative를 주고 front에게 z-index를 1 주었다. 

 

이론상 완벽한 카드인데 전혀 의도한 것처럼 B 라는 뒷면이 화면에 나타나지 않는 것을 확인할 수 있다. 

 

이유는 노란색 영역은 perspective 옵션을 통해서 3차원이 되었지만 안에 있는 card는 속성을 가지고 있지 

않기 때문이다.

 

이것을 해결하기 위해서 card 역시 3차원으로 설정해줄 필요가 있다. 

  .card {
    // ...

    transform-style: preserve-3d;
  }

 

transform-style의 preserve-3d는 영역을 3차원으로 바꿔주는 속성이다. 

추가로 카드에게도 뒷면은 보이지 않게끔 설정해줄 필요가 있다. 

 

transform-style만 설정해주면 3차원 설정을 되지만 카드를 뒤집었을 때 앞면의 뒷부분이 노출되는 것은 동일하다. 

그러니 제대로 뒷면을 나오게 하려면 뒷 화면은 노출되지 않게 설정을 해주어야 한다. 

 

  .card-side {
    // ...

    backface-visibility: hidden;
  }

 

backface-visibility 속성을 사용하면 요소의 뒷면이 사용자에게 보일지 말지를 설정할 수 있다. 

hidden 속성을 준다면 요소의 뒷부분이 사용자에게 보이지 않는다. 

 

이것으로 rotateY(180deg)를 통해 뒷면이 노출되면 사용자에게 나오지 않는 것이다. 

추가적으로 뒷면인 B 부분도 같이 180도 돌아가기 때문에 뒷면이 보이지 않는다. 

그러므로 뒷면만 따로 180도를 돌려주면 뒤집혔을 때 뒷면은 정면이 되므로 화면에 제대로 노출된다. 

  .card-side-back {
    transform: rotateY(180deg); 

    background: blue;
  }

 

이렇게 설정해준다면 뒤집히면 정면을 보고 있는 뒷 배경이 화면에 노출될 것이다. 

 

완성된 부분은 조금의 디테일을 위해서 수정이 했는데 뒤집히는 것을 자연스럽게 하기 위해서 카드에서 

transform-origin을 left로 줘서 뒤집는 모션을 만들어줬고 이때 뒤집히는 각도를 180deg가 아닌 -180deg를 줘서 

매끄럽게 뒤집어지도록 변경하였다. 

<!DOCTYPE html>
<html lang="ko">
  <head>
   // ...

    <style>
      .world {
        display: flex;
        align-items: center;
        justify-content: center;

        width: 80vw;
        height: 80vh;

        background: yellow;

        perspective: 500px;
      }
      .card {
        position: relative;

        width: 100px;
        height: 150px;

        margin: 1em;

        transition: 1s;
        transform: rotateY(0);

        transform-style: preserve-3d;
        transform-origin: left;
      }

      .world:hover .card {
        transform: rotateY(-180deg);
      }

      .card-side {
        position: absolute;
        top: 0;
        left: 0;

        width: 100%;
        height: 100%;

        display: flex;
        align-items: center;
        justify-content: center;

        border-radius: 0.5rem;

        font-size: 1.5rem;
        color: white;

        backface-visibility: hidden;
      }

      .card-side-front {
        z-index: 1;

        background: red;
      }

      .card-side-back {
        transform: rotateY(180deg);

        background: blue;
      }
    </style>
  </head>
  <body>
    <div class="world">
      <div class="card">
        <div class="card-side card-side-front">F</div>
        <div class="card-side card-side-back">B</div>
      </div>
    </div>
  </body>
</html>

 

전체 코드 !

 

문제 해결하기 

지금까지의 작업은 모두 크롬을 기준으로 작업했다. 하지만 브라우저는 많고 브라우저마다 사용할 수 없는

속성이 있다. 

 

해당 기능이 정상 동작하기 위해서 브라우저별로 수정해 보자. 

iOS 

backface-visibility 속성이 크롬에서는 제대로 동작하지만 iOS 환경에서는 제대로 동작하지 않는 문제가 있다. 

다행히 이것은 해결하기 간단한데, iOS에 맞춰서 스타일 속성을 추가해 주면 된다. 

  .card-side {
    // ...

    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
  }

 

-webkit- 키워드를 통해서 iOS에서 지원하는 backface-visibility 속성을 사용하면 문제없이 지원한다. 

 

IE

 

IE11에서는 transform-style: preserve-3d 속성을 지원하지 않는다... 

그래서 애니메이션 효과를 봤을 때도 의도했던 애니메이션이 나오지 않는 것을 확인할 수 있다. 

 

이것을 해결하기 위해서 구조와 스타일을 변경해야 할 필요가 있다. 

  <body>
    <div class="world">
      <div class="card-side card-side-front">F</div>
      <div class="card-side card-side-back">B</div>
    </div>
  </body>

 

먼저 부모 요소였던 card에서 뒤집는 부분이 자식에게 전파가 안 되는 문제가 있었으니 변경해서 world 아래에 

바로 앞면과 뒷면을 배치했다.

 

그리고 위치를 잡아주던 card가 없어졌으므로 위치와 애니메이션 딜레이 요소도 다시 잡아줬다 : 

  .card-side {
    position: absolute;
    top: 50%;
    left: 50%;

    margin-top: -75px;
    margin-left: -50px;

    width: 100px;
    height: 150px;

    display: flex;
    align-items: center;
    justify-content: center;

    border-radius: 0.5rem;

    font-size: 1.5rem;
    color: white;

    transition: 1s;

    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
  }

 

가운데 정렬을 위해서 transform의 translate을 사용해도 되지만 transform에서는 rotate를 사용하고 있고 이어 수정할

위치 변경하는 부분도 있어서 복잡해질 수 있는 부분이라 margin을 통해서 가운데 정렬을 해주었다. 

 

  .card-side-front {
    z-index: 1;
    transform: rotateY(0);

    transform-origin: left;

    background: red;
  }

  .card-side-back {
    transform-origin: right;

    transform: translateX(-100px) rotateY(180deg);

    background: blue;
  }
  
  .world:hover .card-side-front {
    transform: rotateY(-180deg);
  }

  .world:hover .card-side-back {
    transform: translateX(-100px) rotateY(0deg);
  }

 

마지막으로 애니메이션 부분도 변경되었다. 

우선 뒷면은 기본적으로 180deg가 미리 되어 있었다는 부분 때문에 그대로 작업하면 애니메이션이 이상하게 

깨지는 문제가 있었다. 

 

그래서 중심점을 right로 바꾸고 180deg를 한 다음에 호버를 하면 0으로 바꿔서 다시 원래자리로 돌아오게 했다. 

이렇게 하면 앞면은 0deg에서 180deg로 바뀌면서 왼쪽으로 넘어가고 

뒷면은 180deg에서 다시 0으로 바뀌니깐 왼쪽으로 넘어가면서 동일한 애니메이션을 만들 수 있었다. 

 

하지만 뒷면은 미리 뒤집어지므로 100px 오른쪽으로 이동했으니 이것을 앞면과 맞추기 위해서 -100px translate으로

이동시켜주어서 동일한 애니메이션을 만들어주었다. 

 

다른 브라우저에서 동작하지 않는 문제를 해결하기 위해서 꽤나 복잡한 작업을 했지만 이것으로 iOS와 IE, 크롬에서

동작하는 카드 뒤집기 애니메이션을 만들 수 있었다. 

 

전체 코드 

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>3D3</title>

    <style>
      .world {
        position: relative;

        display: flex;
        align-items: center;
        justify-content: center;

        width: 80vw;
        height: 80vh;

        background: yellow;

        perspective: 500px;
      }

      .world:hover .card-side-front {
        transform: rotateY(-180deg);
      }

      .world:hover .card-side-back {
        transform: translateX(-100px) rotateY(0deg);
      }

      .card-side {
        position: absolute;
        top: 50%;
        left: 50%;

        margin-top: -75px;
        margin-left: -50px;

        width: 100px;
        height: 150px;

        display: flex;
        align-items: center;
        justify-content: center;

        border-radius: 0.5rem;

        font-size: 1.5rem;
        color: white;

        transition: 1s;

        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
      }

      .card-side-front {
        z-index: 1;
        transform: rotateY(0);

        transform-origin: left;

        background: red;
      }

      .card-side-back {
        transform-origin: right;

        transform: translateX(-100px) rotateY(180deg);

        background: blue;
      }
    </style>
  </head>
  <body>
    <div class="world">
      <div class="card-side card-side-front">F</div>
      <div class="card-side card-side-back">B</div>
    </div>
  </body>
</html>
반응형
저작자표시 비영리 변경금지

'Web' 카테고리의 다른 글

3D 애니메이션 심화  (2) 2024.09.08
CSS 레이아웃 시스템의 변화  (1) 2024.06.05
CSS 선택자  (2) 2024.05.05
Animation - frame by frame  (3) 2024.05.01
Animation  (5) 2024.04.28
  1. 개요.
  2. 3D 카드 뒤집기. 
  3. 애니메이션 만들기.
  4. 문제 해결하기 
  5. iOS 
  6. IE
  7. 전체 코드 
'Web' 카테고리의 다른 글
  • 3D 애니메이션 심화
  • CSS 레이아웃 시스템의 변화
  • CSS 선택자
  • Animation - frame by frame
잉여개발자
잉여개발자
풀스택 개발자를 목표로 잉여롭게 개발 공부도 하면서 다양한 취미 생활도 즐기고 있는 잉여 개발자입니다.
  • 잉여개발자
    잉여로운 개발일지
    잉여개발자
    • 분류 전체보기 (789)
      • 개발정보 (36)
      • 개발환경 (7)
      • 개발생활 (19)
      • React (141)
        • 이론 (23)
        • 기능 (12)
        • 실험실 (88)
        • 버그 (6)
        • 패스트캠퍼스 (9)
        • Npm (3)
      • React Native (28)
        • 공통 (6)
        • TypeScript (3)
        • JavaScript (18)
        • 버그 (1)
      • Next.js (30)
        • 이론 (13)
        • 실험실 (13)
        • 버그 (3)
      • Web (35)
      • 알고리즘 (202)
        • 풀이 힌트 (39)
      • JavaScript (47)
      • TypeScript (29)
        • 기초 (27)
        • 실험실 (2)
      • Node.js (13)
        • 이론 (0)
        • 기능 (3)
        • 실험실 (9)
        • 버그 (1)
      • 도커 (4)
      • CCNA (22)
        • 이론 (4)
        • 문제 (18)
      • 취미생활 (167)
        • 잉여로운 칵테일 (2)
        • 잉여의 식물키우기 (130)
        • 잉여로운 여행기 (11)
        • 잉여의 제2외국어 (21)
        • 잉여로운 책장 (2)
      • Java (1)
        • Java의 정석 (1)
      • 꿀팁 공유 (3)
  • 태그

    ReactNative
    CSS
    react
    Docker
    타일러영어
    자바스크립트
    redux
    바질 키우기
    ChatGPT
    next.js
    영어독학
    알고리즘
    식물
    Node.js
    리얼클래스
    프로그래머스
    다이소
    덤프
    영어회화
    CCNA
    javascript
    webpack
    리액트
    바질
    타입스크립트
    Babel
    리얼학습일기
    네트워크
    네이버 부스트캠프
    typescript
  • hELLO· Designed By정상우.v4.10.1
잉여개발자
CSS 3D

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.