2020. 12. 27. 00:58ㆍJavascript/시행착오
최근에 프론트앤드 업무를 하면서 3D 작업에 관심을 가지게 됐다. CSS를 이용해서 3D 효과를 많이 이용해보지 않았기 때문에 연습삼아 올려볼까 한다. 이번 포스팅에서 만들어 볼 예제는 캐러셀 슬라이드(Carousel Slide)다.
완성본 영상처럼, 나는 1부터 9까지 총 9개의 아이템을 만들어 줄 것이다. 현재 실행 결과와 코드는 다음과 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 이미지 캐러셀</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="scene">
<div class="carousel>
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>
</div>
</body>
</html>
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
.item{
width: 210px;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid black;
color:#fff;
font-size: 32px;
font-weight: 600;
}
.item:nth-child(1){
background-color: #ff7979;
}
.item:nth-child(2){
background-color: #ffbe76;
}
.item:nth-child(3){
background-color:#f0932b;
}
.item:nth-child(4){
background-color:#7ed6df;
}
.item:nth-child(5){
background-color:#e056fd;
}
.item:nth-child(6){
background-color: #22a6b3;
}
.item:nth-child(7){
background-color: #be2edd;
}
.item:nth-child(8){
background-color:#4834d4;
}
.item:nth-child(9){
background-color:#30336b;
}
이제 현재 공간을 2D에서 3D로 변환시켜야한다. 이를 위해서는 화면에 원근감을 줘야한다. 원근감이란, 멀고 가까운 거리에 대한 느낌. 미술에서는 색채, 명암, 선 따위를 이용하여 원경, 중경, 근경의 느낌을 나타낸다[출처: 네이버 국어사전].
이를 위해 item 요소의 부모인 scene 태그에 perspective 속성을 줘야한다.
.scene{
perspective: 1000px;
}
똑같은 크기의 카드를 보더라도, 가까이 볼 때와 멀리서 볼 때의 크기는 다르게 보인다. perspective 속성 값을 높게 줄 수록, 원근감의 영향을 덜 받고 왜곡이 적다. 따라서 1000px의 값을 줬다. 하지만 이 속성을 부여했다해서 결과는 [그림 1]과 다를게 없을 것이다.
본론으로 돌아와보자. 현재 캐러셀 아이템은 9 개다. 2D가 아니라 z축이 개입된 3D 환경이기 때문에 1부터 9까지 카드를 포개듯이 위치를 겹쳐야한다(그리고 외관상 보기 편하게하기 위해 수직, 수평 모두 가운데 정렬을 속성을 줬다). 이를 위한 CSS 코드와 적용된 결과는 다음과 같다.
.scene{
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
perspective: 1000px;
transform-style: preserve-3d;
}
.carousel{
width: 210px;
height: 150px;
position: relative;
}
.item{
position: absolute;
top: 0;
left: 0;
width: 210px;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid black;
color:#fff;
font-size: 32px;
font-weight: 600;
}
정구각형 형태로 나타내기 위해서는 Y축에 40(360/9)만큼의 각도를 부여해주면 된다.
.item:nth-child(1){
background-color: #ff7979;
transform: rotateY(0deg);
}
.item:nth-child(2){
background-color: #ffbe76;
transform: rotateY(40deg);
}
.item:nth-child(3){
background-color:#f0932b;
transform: rotateY(80deg);
}
.item:nth-child(4){
background-color:#7ed6df;
transform: rotateY(120deg);
}
.item:nth-child(5){
background-color:#e056fd;
transform: rotateY(160deg);
}
.item:nth-child(6){
background-color: #22a6b3;
transform: rotateY(200deg);
}
.item:nth-child(7){
background-color: #be2edd;
transform: rotateY(240deg);
}
.item:nth-child(8){
background-color:#4834d4;
transform: rotateY(280deg);
}
.item:nth-child(9){
background-color:#30336b;
transform: rotateY(320deg);
}
결과는 다음과 같다.
[그림 4]를 보자. 왼쪽은 단순히 위 코드를 실행시켰을 경우의 그림이고, 오른쪽은 .scene 클래스에서 x축 방향으로 85도(45도)만큼의 각도를 준 그림이다. 오른쪽 그림을 보여준 이유는 각 블록을 정구각형 형태로 만들기 위해서다. [그림 4]의 오른쪽 그림을 자세히 보면 다음과 같다.
[그림 5]는 정구각형이다. 그리고 우리가 이상적으로 생각하는 캐러셀 슬라이드를 만들기 위해서는 정 구각형의 각 모서리의 너비가 210픽셀이 되야한다. [그림 4]를 보면 가운데 꼭지점을 중심으로 [그림 5]와 같이 꼬여있는 형태가 되어있는 것을 알 수 있다. 따라서 우리는 원근법을 이용해서 파란색 선분이 있는 위치를 노란색 화살표 길이만큼 이동하여 정구각형 모서리를 210px만큼 만들어줘야한다. 노란색 선분 길이를 구하기 위해서는 간단하게 삼각함수를 이용하면 된다.
노란색 선분을 구하기 위해서는 [그림 6]을 보면 된다. 밑면의 길이는 210픽셀이다. 이를 반으로 나누면 105픽셀이 된다. 그리고 삼각형의 각도는 40도지만, 반으로 나누면 20도가 된다. 따라서 선분의 값은 105/tan20의 값인 288.485가 된다. 따라서 각 아이템 값에 translateZ(288px) 속성을 부여하면 된다. 그리고 각 블록마다 간격을 주기 위해서 너비 값을 210픽셀에서 200픽셀로 바꾸었다.
캐러셀 슬라이드가 완성됐다. 회전을 주기위해서는 y축 방향으로 40도씩 이동을 하면된다. 아니면 애니메이션 값을 주면 된다.
.carousel{
width: 220px;
height: 150px;
position: relative;
transform-style: preserve-3d;
animation: spin 5s infinite linear;
}
@keyframes spin{
from{
transform: rotateY(0deg);
}
to{
transform: rotateY(360deg);
}
}
.scene 클래스 안에서 transform-style:preserve-3d 값을 .carousel 클래스 안으로 설정해주었다. 왜냐하면 .scene클래스 안에서 저 값을 설정해줄 경우, 캐러셀 슬라이더가 회전하면서 블록 사이즈가 유동적으로 바뀌기 때문이다. 전체 코드는 다음과 같다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 이미지 캐러셀</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="scene">
<div class="carousel">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>
</div>
</body>
</html>
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
.scene{
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
perspective: 1200px;
}
.carousel{
width: 220px;
height: 150px;
position: relative;
transform-style: preserve-3d;
animation: spin 5s infinite linear;
}
.item{
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 150px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid black;
color:#fff;
font-size: 32px;
font-weight: 600;
}
.item:nth-child(1){
background-color: #ff7979;
transform: rotateY(0deg) translateZ(288px);
}
.item:nth-child(2){
background-color: #ffbe76;
transform: rotateY(40deg) translateZ(288px);
}
.item:nth-child(3){
background-color:#f0932b;
transform: rotateY(80deg) translateZ(288px);
}
.item:nth-child(4){
background-color:#7ed6df;
transform: rotateY(120deg) translateZ(288px);
}
.item:nth-child(5){
background-color:#e056fd;
transform: rotateY(160deg) translateZ(288px);
}
.item:nth-child(6){
background-color: #22a6b3;
transform: rotateY(200deg) translateZ(288px);
}
.item:nth-child(7){
background-color: #be2edd;
transform: rotateY(240deg) translateZ(288px);
}
.item:nth-child(8){
background-color:#4834d4;
transform: rotateY(280deg) translateZ(288px);
}
.item:nth-child(9){
background-color:#30336b;
transform: rotateY(320deg) translateZ(288px);
}
@keyframes spin{
from{
transform: rotateY(0deg);
}
to{
transform: rotateY(360deg);
}
}
참고자료
'Javascript > 시행착오' 카테고리의 다른 글
[JS] Date() 객체를 이용한 시간 차이 구하기 (0) | 2020.10.03 |
---|---|
[JS] Array.fill([]) 함수와 Copy by Value, Reference (0) | 2020.06.20 |