IT일상

CSS로 3D 회전하는 효과 만들기

  • 프론트엔드
Profile picture

Written by solo5star

2023. 2. 18. 22:44

우아한테크코스에서 미션이 도착하였습니다! 프론트엔드, 백엔드, 안드로이드 과정들은 웹 기초 강의를 듣고 아래와 같은 형식으로 페이지를 제작해야 합니다.

2

프론트 과정만 미션을 하는 것이 아니라 백엔드, 안드로이드 과정도 이 미션을 한다고? 프론트엔드의 자존심을 걸고 백엔드, 안드로이드보다 화려하게 만들겠다 다짐했습니다.

3D 효과를 넣으면 화려하지 않을까? 하는 생각에 3D 효과를 찾아보았고, 아래와 같은 예제를 발견할 수 있었습니다.

3
3D Rotator Gallery (https://www.cssscript.com/3d-rotator-gallery/)

이걸 기울이고 넓게 하면 위성 고리처럼 만들 수 있을 것 같다.. 는 생각에! 바로 실행에 옮겼습니다.

index.html
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="main.css">
  <title>Document</title>
</head>
<body>
  <div class="satellites">
    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
  </div>
</body>
</html>

HTML 하나 만들어줍니다. main.css 도 추가해주고요,

main.css
body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100vh;
}

.satellites {
    
}

.satellite {
  width: 100px;
}

body는 화면 크기로 설정해줍니다. 이미지 크기는 100px로 해주시구요.

4

그러면 이렇게 나올겁니다.

먼저, .satellite 를 감싸고 있는 .satellites 컨테이너를 회전시켜 볼까요?

main.css
 .satellites {
+  border: 4px solid black;
+  animation: rotate-axis 10s linear infinite;
 }
 
+@keyframes rotate-axis {
+  from {
+    transform: rotateY(0deg);
+  }
+  to {
+    transform: rotateY(360deg);
+  }
+}

.satellites 컨테이너는 회전시키는 축이 됩니다. animation을 주어 Y축을 기준으로 0~360도 회전하도록 하였구요.

5

X, Y, Z 축에 따라 어떻게 회전하는지는 위의 그림을 참고해주세요!

축이 잘 회전합니다. 근데 3D라는 느낌이 전혀 없죠? 뭔가 평면상에서 돌아가고 있다는 느낌만 줍니다. 왜냐면, 사용자와 객체의 거리가 설정되어 있지 않기 때문입니다.

사용자와 객체의 거리가 멀면 원근법으로 인해 3D 효과가 부각될 거에요. 이러한 효과를 주는 perspective 속성을 추가해볼까요?

main.css
 @keyframes rotate-axis {
   from {
-    transform: rotateY(0deg);
+    transform: perspective(800px) rotateY(0deg);
   }
   to {
-    transform: rotateY(360deg);
+    transform: perspective(800px) rotateY(360deg);
   }
 }

perspective(800px) 가 추가되었습니다. 사용자와 객체의 거리를 800px로 둡니다. 값을 줄이면 엄청 가까이서 회전하는 것처럼 보일거에요.

오! 뭔가 멀리서 회전하고 있는 느낌을 줍니다.

이제 여기서, 회전하는 .satellites 컨테이너에서 JS 그림의 위치를 띄워볼까요?

main.css
 .satellites {
   border: 4px solid black;
   animation: rotate-axis 10s linear infinite;
+  transform-style: preserve-3d;
 }
 
 .satellite {
   width: 100px;
+  transform: translateZ(100px);
 }

.satellites 컨테이너에는 transform-style: preserve-3d 를 추가하였습니다. 이는 자식 요소에 3D 공간 효과를 줄 수 있도록 할 수 있습니다.

.satellite 에는 transform: translateZ(100px) 을 추가하였습니다. Z축을 기준으로, .satellites 컨테이너에서 100px 만큼 거리를 두게 됩니다.

8

이제 각 JS 이미지들을 빙 둘러싸는 느낌으로 회전시켜보겠습니다.

우선은, .satellite 가 중앙에 있어야 합니다. 아래와 같이 작성하여 중앙으로 위치시켜 봅시다.

main.css
 .satellites {
   border: 4px solid black;
   animation: rotate-axis 10s linear infinite;
   transform-style: preserve-3d;
+  display: flex;
+  justify-content: center;
 }
 
 .satellite {
+  position: absolute;
   width: 100px;
   transform: translateZ(100px);
 }

.satellites 컨테이너를 flex로 만들고 자식 요소들이 중앙에 올 수 있도록 justify-content: center 를 추가했습니다.

.satellite 는 서로 겹칠 수 있도록 position: absolute 를 추가했습니다.

index.html
   <div class="satellites">
-    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 0" src="https://cdn.simpleicons.org/javascript">
-    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 60" src="https://cdn.simpleicons.org/javascript">
-    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 120" src="https://cdn.simpleicons.org/javascript">
-    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 180" src="https://cdn.simpleicons.org/javascript">
-    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 240" src="https://cdn.simpleicons.org/javascript">
-    <img class="satellite" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 300" src="https://cdn.simpleicons.org/javascript">
   </div>

인라인 스타일과 --i 라는 CSS variable 이 추가되었습니다. 각 요소가 Y축 기준으로 몇 deg만큼 회전할 지는 이것을 기준으로 합니다.

main.css
 .satellite {
   position: absolute;
   width: 100px;
-  transform: translateZ(100px);
+  transform: rotateY(calc(var(--i) * 1deg)) translateZ(100px);
 }

transform: rotateY가 추가되었습니다. 계산식이 복잡해 보이지만 --i 값을 기준으로 회전한다고 보면 됩니다.

오, 드디어 의도했던 대로 회전합니다! 이제, 요소를 늘리고 더 넓게 회전할 수 있도록 코드를 수정해보겠습니다.

index.html
   <div class="satellites">
     <img class="satellite" style="--i: 0" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 20" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 40" src="https://cdn.simpleicons.org/javascript">
+    <!-- ... -->
     <img class="satellite" style="--i: 300" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 320" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 340" src="https://cdn.simpleicons.org/javascript">
   </div>
main.css
 .satellite {
   position: absolute;
   width: 100px;
-  transform: rotateY(calc(var(--i) * 1deg)) translateZ(100px);
+  transform: rotateY(calc(var(--i) * 1deg)) translateZ(500px);
 }

👏👏👏

JS 아이콘들이 위아래로 흩어져 있도록 하고 싶다면, CSS에서 top 을 조정하면 됩니다.

index.html
   <div class="satellites">
-    <img class="satellite" style="--i: 0" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 0; top: 80px" src="https://cdn.simpleicons.org/javascript">
-    <img class="satellite" style="--i: 20" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 20; top: 30px" src="https://cdn.simpleicons.org/javascript">
-    <img class="satellite" style="--i: 40" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 40; top: 120px" src="https://cdn.simpleicons.org/javascript">
     <!-- ... -->
-    <img class="satellite" style="--i: 300" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 300; top: 20px" src="https://cdn.simpleicons.org/javascript">
-    <img class="satellite" style="--i: 320" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 320; top: 120px" src="https://cdn.simpleicons.org/javascript">
-    <img class="satellite" style="--i: 340" src="https://cdn.simpleicons.org/javascript">
+    <img class="satellite" style="--i: 340; top: 20px" src="https://cdn.simpleicons.org/javascript">
   </div>

아주 좋네요~!

여기서 회전하는 중심인 .satellites 컨테이너 자체를 기울일 수도 있겠죠?

main.css
 @keyframes rotate-axis {
   from {
-    transform: perspective(800px) rotateY(0deg);
+    transform: rotateZ(350deg) perspective(800px) rotateY(0deg);
   }
   to {
-    transform: perspective(800px) rotateY(360deg);
+    transform: rotateZ(350deg) perspective(800px) rotateY(360deg);
   }
 }

transform: rotateZ 를 추가했습니다. Z 축을 기준으로 회전하면 살짝 기울인 것처럼 보일거에요.

JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript
JavaScript

전체 소스코드

index.html
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="main.css">
  <title>Document</title>
</head>
<body>
  <div class="satellites">
    <img class="satellite" style="--i: 0; top: 80px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 20; top: 30px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 40; top: 120px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 60; top: 70px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 80; top: 10px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 100; top: 90px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 120; top: 40px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 140; top: 30px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 160; top: 20px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 180; top: 90px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 200; top: 110px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 220; top: 20px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 240; top: 70px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 260; top: 80px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 280; top: 10px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 300; top: 20px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 320; top: 120px" src="https://cdn.simpleicons.org/javascript">
    <img class="satellite" style="--i: 340; top: 20px" src="https://cdn.simpleicons.org/javascript">
  </div>
</body>
</html>
main.css
body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100vh;
  padding-top: 20rem;
}

.satellites {
  border: 4px solid black;
  animation: rotate-axis 10s linear infinite;
  transform-style: preserve-3d;
  display: flex;
  justify-content: center;
}

@keyframes rotate-axis {
  from {
    transform: rotateZ(350deg) perspective(800px) rotateY(0deg);
  }
  to {
    transform: rotateZ(350deg) perspective(800px) rotateY(360deg);
  }
}

.satellite {
  position: absolute;
  width: 100px;
  transform: rotateY(calc(var(--i) * 1deg)) translateZ(500px);
}

읽어주셔서 감사합니다~! 디테일한 부분까지는 다루지 않았지만 ... 이로서 위성 고리 애니메이션을 만들 수 있었습니다. 이 글에서는 HTML, CSS 만 사용하여 만들었지만 JS를 사용하여 랜덤한 값으로 흩어지게 해도 좋을 것 같습니다.


Profile picture

Written by solo5star

안녕하세요 👋 개발과 IT에 관심이 많은 solo5star입니다

  • GitHub
  • Baekjoon
  • solved.ac
  • about
© 2023, Built with Gatsby