📝

16. perspective (원근법)

 
원근법을 이용하면 좀 더 다양한 예제를 실행할 수 있습니다. 우선 예제 하나 보고 시작하도록 하겠습니다.
 
여기서 카드가 저 멀리서 앞으로 튀어나오는 원근감은 perspective 800px이 들어있기 때문입니다.
.cont-card { display: flex; align-items: center; justify-content: center; perspective: 800px; }
 
원근감이 있고 없고의 차이를 실습하도록 하겠습니다.
<!DOCTYPE html> <html lang="ko"> <head> <style> .container { position: relative; width: 500px; height: 500px; margin-top: 500px; margin-left: 500px; } .y { position: absolute; transform: rotate(270deg); top: -300px; left: -250px; width: 500px; } .z { position: absolute; transform: rotate(140deg); top: 78px; left: -352px; width: 400px; } .line { display: flex; gap: 0.5em; align-items: center; margin: 50px 0; } .line::before { content: ' '; flex-grow: 1; height: 3px; background-color: black; } .box-container{ /* 주석을 풀어보세요. */ /* perspective: 500px; */ position: relative; top: -260px; width: 200px; height: 200px; background-color: darkcyan; opacity: 0.3; } .box { position: absolute; top: -20px; left: 0; width: 220px; height: 220px; background-color: black; opacity: 0.7; /* Chrome 개발자 도구 열어서 수치를 증가시켜보세요. */ transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg); } </style> </head> <body> <div class="container"> <div class="line x">line x</div> <div class="line y">line y</div> <div class="line z">line z</div> <div class="box-container"> <div class="box"></div> </div> </div> </body> </html>
 
간단한 실습을 해보도록 하겠습니다.
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>perspective</title> <style> .원본 { width: 200px; height: 200px; border: 1px solid black; margin: 100px auto; perspective: 400px; } .회전패널 { width: 200px; height: 200px; background: aqua; /* 실습 1 */ /* transform: rotate(45deg); */ /* 실습 2 perspective를 준것과 주지 않은것을 비교해보세요. */ /* transform: rotateX(45deg); */ /* 실습 3 perspective를 준것과 주지 않은것을 비교해보세요.*/ /* transform: rotateY(45deg); */ } </style> </head> <body> <div class="원본"> <div class="회전패널"></div> </div> </body> </html>
 
X축과 Y축, Z축 회전은 아래 그림을 참고해주세요.
notion imagenotion image
 
어때요? 감이 오시나요? perspective는 우리가 대상을 보는 거리입니다. 라이켓이 저 사각형을 보는 거리라고 생각해주세요. 값이 적을수록 더 가까이 보게 되므로, 효과가 더 극적으로 나타나게 됩니다.
 
그런데 아래 코드를 확인해보시면 의아하실 거에요.
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>perspective</title> <style> .원본 { display: flex; justify-content: space-between; width: 1000px; height: 200px; border: 1px solid black; margin: 100px auto; perspective: 400px; } .회전패널 { width: 200px; height: 200px; background: aqua; transform: rotateY(45deg); } </style> </head> <body> <div class="원본"> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> </div> </body> </html>
notion imagenotion image
 
왜 같은 각도로 움직였는데 똑같은 박스로 나오지 않았을까요? 이는 소실점(vanishing point)을 바라보고 틀어지는 각도가 다르기 때문입니다. 소실점은 중앙이 맞습니다.
아래 코드를 실행해보시면 맨 왼쪽에 있는 카드가 보이지 않는 것을 확인하실 수 있습니다. 완전히 세로로 배치되어 보이지 않게 된 것이죠. 1px이라도 보여야 하는 것 아니냐라고 하실 수 있지만 두깨가 없으므로 보이지 않는 것이 맞습니다.(물리적인 법칙에서 생각하시면 안됩니다.)
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>perspective</title> <style> .원본 { display: flex; width: 1000px; height: 800px; flex-wrap: wrap; border: 1px solid black; margin: 100px auto; perspective: 400px; } .회전패널 { width: 200px; height: 200px; background: aqua; transform: rotateY(45deg); } </style> </head> <body> <div class="원본"> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> <div class="회전패널"></div> </div> </body> </html>
 
여기서 소실점을 한 번 보죠.
notion imagenotion image
 
여러분은 저 빨간 원이 소실점이라고 생각하실 것입니다. 그러나 소실점은 정 중앙입니다! 정 중앙에서 45도씩 양수로 움직였기 때문에 그렇게 보이는 것이죠. 이는 45도를 -45도로 그리고 마지막으로 90도로 바꿔봄으로 알 수 있습니다.
 
이 값을 조정하고 싶다면 perspective-origin을 사용합니다. 초기 값은 perspective-origin: 50% 50%; 입니다. 아래 값을 .원본 class 안에 perspective-origin 속성으로 설정해보세요.
 
perspective-origin: top; perspective-origin: bottom; perspective-origin: 50% 50%; /* 위와 같은 의미입니다. */ perspective-origin: 500px 400px; /* 요소가 15개일때 정중앙 */ perspective-origin: 10% 50%; /* One-value syntax */ perspective-origin: x-position; /* Two-value syntax */ perspective-origin: x-position y-position;
 
여러개로 실습을 해보면 이해가 잘 안될 수 있으니 한개씩 별도로 테스트해볼 수 있도록 코드를 준비해봤습니다. 여기서 각각 해당 박스를 보고 있는 라이켓을 그려보세요!
 
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>perspective</title> <style> .원본 { display: flex; width: 200px; height: 200px; flex-wrap: wrap; border: 1px solid black; margin: 200px auto; } .원본:nth-child(1) { perspective: 400px; perspective-origin: 0px 0px; } .원본:nth-child(2) { perspective: 400px; perspective-origin: 100px 100px; } .원본:nth-child(3) { perspective: 400px; perspective-origin: 200px 200px; } .원본:nth-child(4) { perspective: 400px; perspective-origin: bottom right; } .회전패널 { display: inline-block; width: 200px; height: 200px; background: aqua; perspective: 400px; transform: rotateY(45deg); } </style> </head> <body> <div class="원본"> <div class="회전패널"></div> </div> <div class="원본"> <div class="회전패널"></div> </div> <div class="원본"> <div class="회전패널"></div> </div> <div class="원본"> <div class="회전패널"></div> </div> </body> </html>
 
자, 이제 처음 예제를 다시 가지고 와봤습니다. 여기서 perspective-origin을 바꿔가며 z값을 증가시켜보세요.
<!DOCTYPE html> <html lang="ko"> <head> <style> .container { position: relative; width: 500px; height: 500px; margin-top: 500px; margin-left: 500px; } .y { position: absolute; transform: rotate(270deg); top: -300px; left: -250px; width: 500px; } .z { position: absolute; transform: rotate(140deg); top: 78px; left: -352px; width: 400px; } .line { display: flex; gap: 0.5em; align-items: center; margin: 50px 0; } .line::before { content: ' '; flex-grow: 1; height: 3px; background-color: black; } .box-container{ /* 주석을 풀어보세요. */ /* perspective: 500px; */ position: relative; top: -260px; width: 200px; height: 200px; background-color: darkcyan; opacity: 0.3; } .box { position: absolute; top: -20px; left: 0; width: 220px; height: 220px; background-color: black; opacity: 0.7; /* Chrome 개발자 도구 열어서 수치를 증가시켜보세요. */ transform: translate3d(0px, 0px, 0px) rotateX(0deg) rotateY(0deg) rotateZ(0deg); } </style> </head> <body> <div class="container"> <div class="line x">line x</div> <div class="line y">line y</div> <div class="line z">line z</div> <div class="box-container"> <div class="box"></div> </div> </div> </body> </html>
어려우시죠? 걱정마세요. 실무에서 이런 소실점을 연구하듯 이렇게 살펴보는 일은 없을 것입니다. 제가 가장 많은 힌트를 얻었던 사이트를 첨부합니다.
💡
여러분도 이런 사이트를 만들어보는 것은 어떨까요? 앞에서 한 마크업에 골격잡는 사이트도 좋을 것 같습니다.
 
우리는 간단하게 카드를 뒤집도록 만들 것인데요. 좀 더 다양한 예제를 손 수 해보고 싶다면 아래 튜토리얼을 추천합니다. 코드만 살펴보는 것으로도 큰 도움이 되실 것입니다.
  • transform-style: preserve-3d는 자식요소를 3d 공간처럼 처리합니다. transform-style: flat을 주면 평평하게 보이게 하고요. flat으로 바꾼뒤 뒤집어보세요.
 
우리는 좀 더 쉬운 예제로 살펴보도록 하겠습니다.
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>card</title> <style> /* index.html */ html, body { width: 100%; height: 100%; } .cont-card { display: flex; align-items: center; justify-content: center; perspective: 800px; } .item-card { /* rotateY(720deg);, rotateX(720deg);, rotateZ(720deg); 모두 실습해보세요. */ transform: scale(0.1) rotateY(720deg); width: 500px; height: 500px; background:#F2994A; transition: all 1.2s; } .item-card:hover { width: 500px; height: 500px; background-color: aqua; transform: scale(1) rotateY(0deg); } </style> </head> <body class="cont-card"> <div class="item-card"> <h1>hello world</h1> </div> </body> </html>