국비교육(22-23)

113일차(1)/CSS(12) : 3차원 컨텐츠 만들기(5) / TouchSwipe 활용

서리/Seori 2023. 3. 23. 01:57

113일차(1)/CSS(12) : 3차원 컨텐츠 만들기(5)

 

 

*지난 예제 코드리뷰

 

 

- container 는 전체 브라우저 폭의 80%로 설정해 둠.

 

 

- wrapper의 크기는 이 container의 영향을 받는다.

 

 

- 웹페이지가 resize 되었을때 이 함수를 호출하도록 함!

- resize될 때마다 이 곳으로 실행 순서가 들어온다.

 

- 페이지 로딩시점에 init() 으로 한 번 배치하고, resize될 때마다 다시 배치하는 구조이다.

 

 

- init 안에서는 z축 평행이동값(tz)를 얻어내서 평행이동에 활용하고, 

 angle값은 페이지 로딩시점에 얻어온 값을 활용한다.

 

 

- rY값(회전량)을 변화시키고 함수를 호출해서 회전시킨다.

 

 

- 브라우저에서 돌려보면서 검사창에서 변화하는 rotateY 값을 확인할 수 있다.

 

 

- 위와 같이 일부러 carousel 개체들 간의 거리를 좀 띄우고 싶다면 ?

 

tz=getAngleNtz(n, width).tz+100;

- init() 안에서 이런 형태로 tz에 띄우고 싶은 만큼 값을 더해주면 된다.

 


 

- transition 은 개체가 이동, 전환되는 과정을 보여주는것

- 지금은 회전할때와 재배치할때 모두 transition이 들어가 있는데,

  회전시킬 때만 적용하고 재배치할때는 transition 기능을 뺄 것이다.

 

 

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;
        }
        /*
            class 선택자로 transition을 정의하고 
            필요할 때 이 class를 어떤 요소에 추가하면 transition이 적용되고
            제거하면 transition이 적용되지 않도록 할 수 있다.
        */
        .transition{
            transition: transform 0.4s ease-out;
        }
    </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(){
            //다시 배치할 때는 transition이 필요없다.
            $(".carousel").removeClass("transition");
            //배치할때 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(()=>{
            $(".carousel").addClass("transition");
            rY -= angle;            
            rotateY();
        })
        $("#nextBtn").on("click", ()=>{
            $(".carousel").addClass("transition");
            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;
        }
        //carousel 을 자동 회전시킬지 여부
        let isRun=true;

        $(".carousel").on("mouseover", ()=>{
            isRun=false;
        });
        $(".carousel").on("mouseout", ()=>{
            isRun=true;
        });

        //5초마다 carousel을 회전시키는 인터벌
        let setIntervalId=setInterval(()=>{
            if(!isRun){
                return;
            }

            $(".carousel").addClass("transition");
            rY -= angle;            
            rotateY();

        }, 5000);
    </script>
</body>
</html>

 

 

- 이전에 있던 trasition 은 빼고 transition을 클래스로 만들어줌!

 

 

- 클래스로 만들어 필요할 때에만 적용할 수 있다!

 

 

- .addClass(), .removeClass() 를 사용해 필요한 시점에 add, remove 할수있다. 콘솔창에서 테스트!

 

 

 

- addClass, removeClass 를 사용한 클래스 추가, 제거

(클릭시에 추가하고, init에서 배치할때 제거해준다)

 


 

* Carousel 자동 플레이 기능 추가

- 일정 시간 주기(5초) 마다 자동으로 돌아가게 만들기!

 

 

setInterval(function, 주기)

- 함수 자리에 ()=>{} 으로 원하는 함수를 넣어서 테스트해보기!

 

 

- 콘솔창에 5초 주기로 계속 출력되는 것을 볼 수 있다.

 

 

- rY값과 rotateY() 코드를 setInterval() 안에 넣어주면 5초마다 carousel이 자동 회전한다.

 

- 이 setInterval() 함수는 인터벌의 아이디값을 리턴해준다.

 

let setIntervalId=setInterval(()=>{});

- 리턴된 id값을 변수에 담아서 아래에서 사용할 수 있다.

 

 

- 웹페이지에서 뭔가 움직이는 컨텐츠를 만들었다면 정지하는 기능도 반드시 고려해야 한다.

 (고려하지 않을 경우 웹 접근성에 어긋난다.)

- 이외에도 번쩍거리는 컨텐츠 등도 주의해야 한다..

 

- 이 인터벌을 특정 시점에 취소하거나, 더 이상 carousel이 돌아가지 않게 할 수 있는 방법도 고려해야 한다.

- clearInterval(인터벌 id);  로 인터벌을 취소할 수 있다.

 

 

- 아니면 isRun이라는 변수를 선언해두고, false 조건 하에서는 멈추도록 할 수도 있다.

- 멈추고 싶은 상황이나 지점에서 isRun 변수의 값을 제어해주면 된다.

 

- 움직이는 컨텐츠가 있다면 멈추는 방법도 반드시 고려해주기!

- carousel을 위아래로 회전하게 할 수도 있다. 다양하게 응용해보기.

 


 

* Touch Swipe 기능 사용해보기

 

* 구글에 검색: touch swipe js

- 링크: https://github.com/mattbryson/TouchSwipe-Jquery-Plugin

 

 

- jquery 로딩하기

- jquery 플러그인이므로 jquery에 의존하고 있다.

 

 

- jquery로 $().action() / .on() / .addClass() 등등을 써봤는데,

  이 플러그인을 추가하면 이와 비슷하게 새 동작 .swipe() 를 사용할 수 있게 된다.

 

 

- swipe() 메소드를 사용하면 direction, distance, duration, fingerCount, fingerData 등

 사용자가 화면에 어떤 동작을 했는지 데이터를 얻어낼 수 있다.

 

 

- 깃허브에서 ZIP 압축파일로 받아서 해제해두기

 

 

- jquery.touchSwipe javascript 파일을 복사해서

 

 

- 현재 VS code에서 사용하는 폴더 위치에 넣어준다.

 

 

touchSwipe.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>touchSwipe.html</title>
    <style>
        #touchPad{
            width: 300px;
            height: 300px;
            background-color: antiquewhite;
            border: 1px solid palevioletred;
            border-radius: 10px;
        }
    </style>
</head>
<body>    
    <div class="container">
        <h3>touch 이벤트 처리</h3>
        <div id="touchPad">


        </div>
        phase : <span id="phase"></span><br/>
        direction : <span id="direction"></span><br/>
        distance : <span id="distance"></span><br/>
        coord : <span id="coord"></span>
    </div>

    <!-- jquery 로딩 -->
    <script src="https://code.jquery.com/jquery-3.6.4.js"
        integrity="sha256-a9jBBRygX1Bh5lt8GZjXDzyOB+bWve9EiO7tROUtj/E="
            crossorigin="anonymous"></script>
    <!-- jquery plugin javascript 로딩 -->
    <script src="js/jquery.touchSwipe.js"></script>
    <script>
        $("#touchPad").swipe({
            swipeStatus:function(event, phase, direction, distance){
                /*
                    event 는 발생항 이벤트에 대한 정보를 담고 있는 event object 이다.
                    phase는 위상값
                    direction은 방향값
                    distance는 거리이다.
                */
                console.log(event);
                console.log(phase);
                $("#phase").text(phase);
                $("#direction").text(direction);
                $("#distance").text(distance);
                $("#coord").text(event.x+"|"+event.y);

                if(phase=="start"){
                    //터치 시작일때

                }else if(phase=="move"){
                    //터치 후 움직이는 상태

                }else if(phase=="end"){
                    //터치하고 조금만 움직이고 뗀 상태

                }else if(phase=="cancel"){
                    //터치하고 일정 역치 이상 움직이고 뗀 상태

                }
            }

        });

    </script>
</body>
</html>

 

 

 

- 이 swipe() 는 이 jquery 플러그인이 있어서 사용가능한 함수!

- 깃허브 문서를 보고 사용하는 법을 익히면 된다.

 

 

- touch 이벤트를 사용하기 위해 4개의 값을 조사해볼 것이다. touchSwipe로 4가지 값을 얻어낼 수 있다.

 

1) phase : 위상 
2) direction : 방향
3) distance : 거리 
4) coord : 좌표

 

 

- swipe() 안에는 { } object 를 전달한다.

 

- 이 함수를 호출하면서 div안의 정보에서 필요한 값을 함수에 전달해준다.

- 이벤트, 위상, 방향, 거리 4개의 인자를 넣어준다.

- 이 안에서 필요한 값을 특정 시점에 사용해주면 된다.

 

 

- event phase 콘솔창에 출력해보기

 

 

- #touchPad 개체를 클릭하면 pointerEvent 가 발생한다.

 

 

- pointer down : 마우스를 클릭한 것을 말한다.

 

 

- 클릭하고 드래그하고 떼면  이런 위상이 발생하는 것을 볼 수 있다!

1) start : 시작하는 상태

2) move : 움직이는 상태

3) cancel : 드래그하고 멈춘 상태. 단, 드래그를 거의 안했을 때

4) end : 드래그하고 멈춘 상태. 드래그를 일정량 이상 했을 때

 

 

- 개체 아래에 뽑아내는 값을 출력해보았다.

- distance는 start와 end 사이의 거리이다.

 

- direction : 이동한 방향

- distance : 이동 거리인데, 거리 그 자체가 아니라 그 방향으로 이동한 절대값을 말한다.

 

- phase 의 end와 cancel 을 구분하는 데에는 기준점이 있다.

(드래그해서 75이상 움직여야 end가 뜬다. 그 이하로 움직인것은 취소된 것으로 본다. 75이상 움직여야 의도를 갖고 움직인 것으로 판정하겠다는 뜻!)

 

 

- 이렇게 phase 값에 따라 분기하도록 작성해볼 수 있다.

 

- right, left 값이 필요하다. 이것을 회전값에 반영한다.

- 좌우로 움직일때는 transition 없이, 움직였다가 뗐을 때(회전시킬 때)는 transition이 있도록 설정해보기

 

 


 

test_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>test_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;
        }
        /*
            class 선택자로 transition을 정의하고 
            필요할 때 이 class를 어떤 요소에 추가하면 transition이 적용되고
            제거하면 transition이 적용되지 않도록 할 수 있다.
        */
        .transition{
            transition: transform 0.4s ease-out;
        }
    </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>
    <!-- jquery plugin javascript 로딩 -->
    <script src="js/jquery.touchSwipe.js"></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(){
            //다시 배치할 때는 transition이 필요없다.
            $(".carousel").removeClass("transition");
            //배치할때 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(()=>{
            $(".carousel").addClass("transition");
            rY -= angle;            
            rotateY();
        })
        $("#nextBtn").on("click", ()=>{
            $(".carousel").addClass("transition");
            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;
        }
        //carousel 을 자동 회전시킬지 여부        
        let isRun=true;

        $(".carousel").on("mouseover", ()=>{
            isRun=false;
        });
        $(".carousel").on("mouseout", ()=>{
            isRun=true;
        });

        //5초마다 carousel을 회전시키는 인터벌
        // let setIntervalId=setInterval(()=>{
        //     if(!isRun){
        //         return;
        //     }

        //     $(".carousel").addClass("transition");
        //     rY -= angle;            
        //     rotateY();

        // }, 5000);

        //터치가 시작된 지점의 rY값을 저장할 변수
        let startRY=0;

        $(".carousel").swipe({
            swipeStatus:function(event, phase, direction, distance){
                switch(phase){
                    case "start":
                        $(".carousel").removeClass("transition");
                            //터치가 시작된 지점의 현재 rY값을 변수에 저장한다.
                            startRY=rY;
                    break;
                    case "move":
                        if(direction=="left"){
                            //스와이프한 거리에 비례해서 rY 에 반영해서 회전시킨다.
                            rY = startRY - distance/8;
                        }else if(direction=="right"){
                            rY = startRY + distance/8;
                        }
                    break;
                    case "cancel":
                        $(".carousel").addClass("transition");
                        //취소되었을 때는 원상복구
                        rY=startRY;
                        rotateY();
                    break;
                    case "end":
                        $(".carousel").addClass("transition");
                        if(direction=="left"){
                            rY = startRY - angle;
                            rotateY();
                        }else if(direction=="right"){
                            rY = startRY + angle;
                            rotateY();
                        }
                    break;
                }
            }        
        });
    </script>
</body>
</html>

 

 

- jquery 플러그인 추가

 

 

- 이벤트 발생시 읽어들이는 4개의 인자를 넣어주기

 

 

- 위상값 활용하기. switch문으로 위상의 4가지 경우를 분기해준다!

 

 

- Y축을 중심으로 회전시켜야 한다.(반시계 방향)

- swipe한 방향으로 이동 거리를 읽어내서 반응할 수 있도록

 

 

- 각각 반시계 방향, 시계 방향으로 돌리는 것

 

 

- 이제 마우스 움직임에 따라 조정된다.

 

- rY값을 변화시키므로, 터치가 시작될 때 원상복구할 rY값을 어딘가에 저장해주어야 한다.

 

 

- 변수 startRY 를 만들어주고, 터치가 시작된 시점의 rY 값을 해당 변수에 저장한다.

 

(direction이 아니라 distance이다.)

 

- 여기서 계산한 값을 각도로 사용하는데, direction의 단위는 px이다.

  그런데 100px 움직인 것을 100도로 사용하면 너무 많다.

- 대략 /8 정도로 나눠보기 (100px 움직이면 1/8만큼 이동하도록)

 

 

- 터치가 시작된 지점의 rY값(startRY)과 지금의 rY값을 계산해서 움직이게 한다.

 

 

- 취소 cancel 시 rY값은 원상복구되도록 한다.

 

 

- end일때는 이동한 방향 좌우를 파악해서 이동하기!

- 최초값에서 angle을 더해서 돌릴 것인지 빼서 돌릴 것인지 파악하고 움직이기

 

 

 

- 원래 최초의 rY값을 저장해놓고 기본값으로 활용하는 것이 중요하다!!

 

 

 

- 이렇게 마우스 클릭-드래그로 이동할수있는 Carousel을 만들어보았다.