국비교육(22-23)

112일차(1)/CSS(11) : 3차원 컨텐츠 만들기(4)

서리/Seori 2023. 3. 22. 01:20

112일차(1)/CSS(11) : 3차원 컨텐츠 만들기(4)

 

 

 

- 3d공간을 정의하고, 그 안에 들어 있는 div들을 큐브 형태로 배치함

 

 

- 특정 UI를 페이지 로딩 시점에 3차원 transform을 이용해서 3차원적으로 배치해둔 다음,

  특정 시점부모 요소를 3차원 transform 시키면 배치시켜둔 대로 움직인다.

 

 

- 마우스를 올렸을 때(특정 시점) 부모 div가 통째로 움직이도록 할 수 있다.

- 부모 div에 마우스 hover되었을 때 애니메이션이 적용되도록 한 상태!

 

 

 

- 원래는 겹쳐져 있었는데, 200px만큼 z축 방향으로 밀어버린 것이다.

 

 

- 2번째 div도 90도를 돌리고 200px 을 밀어버린 상태.

- 이렇게 겹쳐있던 div를 회전, 평행 이동시켜 정육면체 모양을 만들 수 있다.

 

 

- 처음에 1 div를 z축으로 밀었기 때문에 확대되어 커진다. 그래서 처음에 큐브를 -200px 만큼 밀어둔 것!

- div 크기의 절반만큼 밀어주어야 모서리가 딱 맞아 떨어진다.

 

 

- 사각형이기 때문에 90도 단위로 변화시키는 것이다. 우측상단 이미지는 위에서 본 것

 

 

- 만약 6각형을 만들고 싶다면? 가운데에 div가 6개 겹쳐져 있다고 하자

- 앞으로 미는 길이를 구하려면 200*tan60 만큼 곱해야 한다.

 

- div를 60도씩 돌리면서 200*tan60 만큼 평행이동시켜야 한다.

 

 

- 이렇게 함수를 만들어서 적용할 수도 있다. 4각형이고 폭이 400인 도형이라면 안에 숫자를 전달해주면 된다.

- 그러면 angle, tz에 알아서 값이 들어간다.

 

- 이 함수가 있으면 몇 도씩 돌리면서 얼마만큼 평행이동시켜야 하는지 알아내기 쉽다.

- 이 함수에서 리턴하는 object 를 사용하면 된다.

 

- div를 직접 배치하는 것이 아니라 원하는 시점에 javascript 로 배치할 수 있도록 할 예정!

 


 

 

- 이 도형을 화면 크기에 따라 다르게 보여주고 싶다면? (너비는 디바이스의 크기에 의존하도록 하면 된다.)

- 같은 코드인데 폭이 넓은 디바이스에서는 우측과 같이 보이도록 하려고 한다.

- 화면의 크기가 동적이라면 3d transform을 다르게 배치해야 한다. javascript로 배치하는 것이 필요한 이유!

- 화면 크기에따라 tz의 양이 달라진다(translateZ) 이것을 javascript로 할 수 있다.

 

- 터치(마우스 드래그)도 가능하게 할 수 있다. 마우스 터치에 반응해 박스를 돌아가게 하는 것!

 

 

- perspective-origin 을 설정해 시선을 바꾸어 볼 수 있다.

 

 

- javascript로 인라인 css를 추가하는 방법도 기억하기!

 


 

* 6각형 도형 배치하기

 

Step06_3dHexagon.html

<!DOCTYPE html>
<html lang="en">
<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>Step06_3dHexagon.html</title>
    <style>
        .wrapper{
            perspective: 500px;
            perspective-origin: 50% 50%;
        }
        .hexa{
            width: 400px;
            height: 400px;
            margin: 0 auto;
            transform-style: preserve-3d;
            transform-origin: 50% 50%;
            transform: translateZ(-346px);
        }
        .hexa > div{
            border: 2px solid palevioletred;
            position: absolute;
            top:0;
            left:0;
            width: 400px;
            height: 400px;
            background-color: antiquewhite;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 100px;
            font-weight: bold;
            opacity: 0.5;
        }

        .hexa > div:nth-child(1){
            transform: translateZ(346px);
        }
        .hexa > div:nth-child(2){
            transform: rotateY(60deg) translateZ(346px);
        }
        .hexa > div:nth-child(3){
            transform: rotateY(120deg) translateZ(346px);
        }
        .hexa > div:nth-child(4){
            transform: rotateY(180deg) translateZ(346px);
        }
        .hexa > div:nth-child(5){
            transform: rotateY(240deg) translateZ(346px);
        }
        .hexa > div:nth-child(6){
            transform: rotateY(300deg) translateZ(346px);
        }
        /* .cube{
            transition: transform 0.4s ease-out;            
        }
        .cube:hover{
            transform: translateZ(-200px) rotateX(45deg) rotateY(90deg); 
        } */

        @keyframes rotateAni{
            0%{
                transform: translateZ(-346px) rotateX(0deg) rotateY(0deg);
            }
            100%{
                transform: translateZ(-346px) rotateX(360deg) rotateY(360deg);
            }
        }
        .hexa:hover{
            animation: rotateAni 5s infinite linear alternate;
        }
    </style>
</head>
<body>
    <div class="container">
        <h3>3d Hexagon 테스트</h3>
        <div class="wrapper">
            <div class="hexa">
                <div>1</div>
                <div>2</div>
                <div>3</div>
                <div>4</div>
                <div>5</div>
                <div>6</div>
            </div>
        </div>
    </div>
    <script>
        //n 각형의 폭을 함수에 전달하면 div 를 배치할때 rotateY 값과 translateZ 가 Object 로 리턴되는 함수 
        function getAngleNtz(n, width) {
            var angle = Math.round(360 / n);
            var tz = Math.round((width / 2) / Math.tan((angle * Math.PI) / (2 * 180)));
            var obj = {
                angle: angle,
                tz: tz
            };
            return obj;
        }
    </script>
</body>
</html>

 

- 기본 정육면체 상태로 만들고 javascript 함수 가져오기

 

 

- 콘솔창에서 만들고자 하는 n, width 값을 넣어 함수로 계산해보면 정확한 값이 계산되어 나온다.

- 6각형은 60도씩 돌면서 346px 이동하면 된다.

 

 

- 클래스명은 hexa로 수정!

 

 

- 60도씩 회전시키고, Z이동은 346px씩 이동시킨다.

 

 

- 이런 형태로 만들어진다.

 

 

- 60도 회전시켜서 밀고, 120도 회전시켜서 밀고 ... 이런 과정으로 만들어진 것

 


 

Step06_3dHexagon2.html

<!DOCTYPE html>
<html lang="en">
<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>Step06_3dHexagon2.html</title>
    <style> 
        .container{
            width: 80%;
            margin: 0 auto;
        } 
        .wrapper{
            perspective: 500px;
            perspective-origin: 50% 50%;
        }
        .hexa{
            width: 100%;
            height: 400px;
            margin: 0 auto;
            transform-style: preserve-3d;
            transform-origin: 50% 50%;
            
        }
        .hexa > div{
            border: 2px solid palevioletred;
            position: absolute;
            top:0;
            left:0;
            width: 100%;
            height: 400px;
            background-color: antiquewhite;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 100px;
            font-weight: bold;
            opacity: 0.5;
        }        

    </style>
</head>
<body>
    <div class="container">
        <h3>3d Hexagon 테스트</h3>
        <div class="wrapper">
            <div class="hexa">
                <div>1</div>
                <div>2</div>
                <div>3</div>
                <div>4</div>
                <div>5</div>
                <div>6</div>
            </div>
        </div>
    </div>
    <!-- jquery 로딩 -->
    <script src="https://code.jquery.com/jquery-3.6.4.js"
        integrity="sha256-a9jBBRygX1Bh5lt8GZjXDzyOB+bWve9EiO7tROUtj/E="
            crossorigin="anonymous"></script>
    <script>
        //몇각형인지
        const n=6;
        //페이지 로딩 시점에 한 변의 길이가 몇인지
        let width=$(".wrapper").width();
        //배치할 때 회전할 각도 얻어내기
        const angle=getAngleNtz(n, width).angle;        

        function init(){
            //배치할때 z 축 평행이동 값 얻어내기
            let tz=getAngleNtz(n, width).tz;

            $(".hexa").css("transform", `translateZ(${-tz}px)`);



            for(let i=0; i<n; i++){
                let nth=i+1;
                $(`.hexa > div:nth-child(${nth})`)
                    .css("transform",`rotateY(${angle*i}deg) translateZ(${tz}px)`);
            }
        }

        //init() 함수를 호출해서 다각형 배치하기
        init();

        //화면이 리사이즈되었을 때 다시 배치하기
        $(window).resize(()=>{
            //폭을 새로 얻어와서 
            width=$(".wrapper").width();
            //다각형 배치하기
            init();
        });

        //n 각형의 폭을 함수에 전달하면 div 를 배치할때 rotateY 값과 translateZ 가 Object 로 리턴되는 함수 
        function getAngleNtz(n, width) {
            var angle = Math.round(360 / n);
            var tz = Math.round((width / 2) / Math.tan((angle * Math.PI) / (2 * 180)));
            var obj = {
                angle: angle,
                tz: tz
            };
            return obj;
        }
    </script>
</body>
</html>

 

 

 

- div에 css를 적요한 이 작업을 javascript로 대체해볼 예정

- 반복문을 돌면서 숫자 값만 바꿔줄 수 있으면 된다.

 

 

- 초기화 함수 init() 만들기.

- 순서대로 다 돌면서 hexa안의 div에 각각 css를 적용해준다.

 

 

- 백틱을 사용하면 문자열을 이어붙이기가 편리하다. ${ } 로 사용가능

 

 

 

- n, width, angle, tz 값을 변수로 정의해주고 변수를 사용하는 구조로 바꿔 준다.(하드코딩 대신)

- 다양한 다각형에 적용할 수 있도록!

 

 

- width 변의 길이를 wrapper를 사용해 동적으로 적용한다.

 

 

 

- 반복문을 돌리기 전에 init 안에서 hexa를 -tz px만큼 밀어놓고 시작하기

 

 

 

$(window).resize()

- 화면의 폭이 달라졌을 때 다시 init을 하도록 설정해준다.

 

 

- 폭이 달라지면 tz 값도 달라져야 하므로, let함수를 정의하고 폭에 맞게 새로 배치되도록 한다.

 

 

- 윈도우 창의 폭에 따라 너비가 다르게 보인다.

 

 


 

 

Step07_Carousel.html

<!DOCTYPE html>
<html lang="en">
<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>Step07_Carousel.html</title>
    <style> 
        .container{
            width: 80%;
            margin: 0 auto;
        } 
        .wrapper{
            perspective: 500px;
            perspective-origin: 50% 50%;
        }
        .carousel{
            width: 100%;
            height: 400px;
            margin: 0 auto;
            transform-style: preserve-3d;
            transform-origin: 50% 50%;
            transition: transform 0.4s ease-out;
        }
        .carousel > div{
            border: 2px solid palevioletred;
            position: absolute;
            top:0;
            left:0;
            width: 100%;
            height: 400px;
            background-color: antiquewhite;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 100px;
            font-weight: bold;
            opacity: 0.5;            
        }        
        nav{
            text-align: center;          
        }
        nav button{
            font-size: 40px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h3>3d Carousel 테스트</h3>
        <div class="wrapper">
            <div class="carousel">                
            </div>
        </div>        
        <nav>
            <button id="prevBtn">&laquo;</button>
            <button id="nextBtn">&raquo;</button>
        </nav>
    </div>
    <!-- jquery 로딩 -->
    <script src="https://code.jquery.com/jquery-3.6.4.js"
        integrity="sha256-a9jBBRygX1Bh5lt8GZjXDzyOB+bWve9EiO7tROUtj/E="
            crossorigin="anonymous"></script>
    <script>
        //몇각형인지
        const n=8;
        //n각형 만큼 div를 동적으로 만들어서 .carousel 안에 넣기
        for(let i=0; i<n; i++){
            //div를 만들어서 innerText를 출력하고 .carousel 에 추가하기
            $("<div>").text(i+1).appendTo(".carousel");
        }
        //페이지 로딩 시점에 한 변의 길이가 몇인지
        let width=$(".wrapper").width();
        //배치할 때 회전할 각도 얻어내기
        const angle=getAngleNtz(n, width).angle;        

        //tz값을 전역변수로 설정
        let tz;
        // .carousel의 회전값
        let rY=0;

        function init(){
            //배치할때 z 축 평행이동 값 얻어내기
            tz=getAngleNtz(n, width).tz;

            $(".carousel").css("transform", `translateZ(${-tz}px)`);

            for(let i=0; i<n; i++){
                let nth=i+1;
                $(`.carousel > div:nth-child(${nth})`)
                    .css("transform",`rotateY(${angle*i}deg) translateZ(${tz}px)`);
            }
            //현재 carousel 의 회전값 반영해주기
            rotateY();
        }

        //init() 함수를 호출해서 다각형 배치하기
        init();

        //화면이 리사이즈되었을 때 다시 배치하기
        $(window).resize(()=>{
            //폭을 새로 얻어와서 
            width=$(".wrapper").width();
            //다각형 배치하기
            init();
        });

        
        //위의 버튼을 눌렀을때 .cube를 90도씩 y축 기준으로 회전하도록 해보세요        
        $("#prevBtn").click(()=>{
            rY -= angle;            
            rotateY();
        })
        $("#nextBtn").on("click", ()=>{
            rY += angle;
            rotateY();
        });

        function rotateY(){
            $(".carousel").css("transform", "translateZ(-"+tz+"px) rotateY("+rY+"deg)");
        }

        //n 각형의 폭을 함수에 전달하면 div 를 배치할때 rotateY 값과 translateZ 가 Object 로 리턴되는 함수 
        function getAngleNtz(n, width) {
            var angle = Math.round(360 / n);
            var tz = Math.round((width / 2) / Math.tan((angle * Math.PI) / (2 * 180)));
            var obj = {
                angle: angle,
                tz: tz
            };
            return obj;
        }
    </script>
</body>
</html>

 

 

 

- 이전 예제를 복사해와서 ctrl+f 로 hexa를 carousel로 모두 바꾸어주기

 

- html 요소로 있는 div를 모두 삭제하고 javascript로 넣어주기

 

 

- div를 만들어서 innerText를 출력하고 .carousel 에 추가하기!

- <div> 요소를 jquery 로 만들어준 것이다.

- 자바스크립트 코드로 바꾸면 document.createElement("div") 가 된다.

 

 

 

- 이제 모두 변수를 사용해 일반화시켜 두었으므로 해당 위치에 숫자만 들어오면 된다.

- 변수 n 에다가 원하는 숫자를 넣어주면 알아서 n각형으로 만들어진다.

 

 

 

- 12각형으로 만들어본 모습!

 

 

 

- 하단에 화살표버튼 nav 요소를 추가하고 css를 추가

 

 

- 화살표 버튼의 클릭 이벤트에 함수를 적용

- rY에 들어갈 회전할 값이 angle 이고, translateZ() 에 들어갈 값이 tz이다.

 

 

- tz값을 여러 함수에서 사용할 수 있도록 전역변수로 설정

 

 

- angle, tz값을 함수에 적용

 

- 단, 이렇게 하면 회전하던 중에 창의 크기가 달라지면 다시 1로 돌아온다는 문제가 있다.

 

 

- 새로 배치할 때 이 값도 활용해야 한다!

- rY값도 전역변수로 설정해준다.

 

 

- 회전 함수(rotateY)를하나 만들어놓고 함수를 호출하는 구조로 바꿔주기

 

- 그리고 이 함수를 init()안에서도 적용한다.

- 새로 배치할 때 같이 배치해 주어야 값이 초기화되는 것을 방지할 수 있다.

 

- transition도 추가해주면 이동할 때 뿐만 아니라 배치할때도 변화과정이 천천히 보인다.

 

- 이제 이 div 안에다가 어떤 컨텐츠를 배치하고 싶으면 높이만 결정해서 배치하면 된다.

 

 

 

- 이렇게 div 안에 각각의 번호 대신 원하는 컨텐츠를 넣을 수 있다.

 

 

backface-visibility: hidden;

- hidden 으로 설정하면 뒤에 있는 것들이 보이지 않도록 할 수 있다. 원하는 이미지를 넣을 수 있다.

- Bootstrap의 carousel 처럼 갤러리 이미지를 하나씩 배치해서 돌아가면서 보여줄 수도 있다.

 

 

- 이제 이 예제를 마우스 터치로 회전시킬 수 있도록 설정할 예정!