개요.
카드를 웹사이트에서 구현한다고 했을 때 뒤집는 애니메이션이 필요한 경우가 있다.
그냥 앞뒤가 변화는 것보다 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 |