본문 바로가기

JavaScript/javascript 활용하기

CSS - 움직이는 문 만들기

 

reset.css 

html, body, h1, h2, h3, h4, h5, h6, p, blockquote, code, img, dl, dt, dd, ol, ul, li, fieldset, legend, caption { margin: 0; padding: 0; border: 0; }
div, span, article, section, header, footer, p, ul, li, fieldset, legend, label, a, nav { box-sizing: border-box; }
html {
    height: 100%;
}
body {
    min-height: 100%;
}
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
}
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
	display: block;
}
ol,
ul {
	list-style: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}

 

 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/reset.css">
    <style>
        .stage{
            width: 100vw;
            height: 100vh;
            background: #333;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .door {
            position: relative;
            width: 100px;
            height: 150px;
            outline: 2px dashed yellow;
        }
        .door-back{
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: black;
        }

     </style>
</head>
<body>
	<div class="stage">
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>         
            <div class="door-body"></div>
        </div>
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>         
            <div class="door-body"></div>
        </div>
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>     
            <div class="door-body"></div>
        </div>
    </div>
    
</body>
</html>

 

 

먼저 강아지 사진을 넣어주기 위해 레이아웃인 stage와 배경인 door-back을 설정해준다. 

 

 

 

그후 강아지의 위치를 대략적으로 잡기 위해 

position relative <-> absolute를 활용한 고정 레이아웃 속성을 통해 위치를 잡아준다 .

.dog{
            position: absolute;
            left: 0;
            bottom: 0;
            width:100px;
            height: 100px;
            background-repeat: no-repeat;
            background-position: 50% 50%;
            background-size: contain;
            outline: 2px dashed red;
        }

 

 

 

 

 

 

그후 이미지를 넣어준다.

 

  .door:nth-child(1) .dog{
            background-image: url("images/dog1.png");
        }
        .door:nth-child(2) .dog{
            background-image: url("images/dog2.png");
        }
        .door:nth-child(3) .dog{
            background-image: url("images/dog3.png");
        }

www.freepngs.com/dog-pngs

(강아지 사진들은 위 사이트에서 다운받았다)

 

 

 

 

이 사진 위를 문으로 덮어준다.

 

        .door-body{
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
        }
        .door:nth-child(1) .door-body{
            background: rgba(255, 0, 0, 0.7);
        }
        .door:nth-child(2) .door-body{
            background: rgba(0, 255, 0, 0.7);
        }
        .door:nth-child(3) .door-body{
            background: rgba(0, 0, 255, 0.7);
        }

 

 

 

 

레이아웃을 잡아줬으니 outline은 제거해준다. 

 

 

이때까지의 html은 다음과 같다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/reset.css">
    <style>
        .stage{
            width: 100vw;
            height: 100vh;
            background: #333;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .door {
            position: relative;
            width: 100px;
            height: 150px;
           
        }
        .door-back{
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: black;
        }
        .dog{
            position: absolute;
            left: 0;
            bottom: 0;
            width:100px;
            height: 100px;
            background-repeat: no-repeat;
            background-position: 50% 50%;
            background-size: contain;
          
        }
        .door:nth-child(1) .dog{
            background-image: url("images/dog1.png");
        }
        .door:nth-child(2) .dog{
            background-image: url("images/dog2.png");
        }
        .door:nth-child(3) .dog{
            background-image: url("images/dog3.png");
        }

        .door-body{
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
        }
        .door:nth-child(1) .door-body{
            background: rgba(255, 0, 0, 0.7);
        }
        .door:nth-child(2) .door-body{
            background: rgba(0, 255, 0, 0.7);
        }
        .door:nth-child(3) .door-body{
            background: rgba(0, 0, 255, 0.7);
        }
     </style>
</head>
<body>
    <div class="stage">
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>         
            <div class="door-body"></div>
        </div>
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>         
            <div class="door-body"></div>
        </div>
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>     
            <div class="door-body"></div>
        </div>
    </div>
    
</body>
</html>

 

강아지 움직이기 

 

먼저 강아지를 오른쪽 -> 왼쪽으로 나타나게 해주려는 모션을 취해주기 위해서는 

 

translate속성을 활용해 미리 강아지 사진을 옮겨놓고 추후 애니메이션으로 나타게 하는 방식으로

진행한다.

이를 위해 강아지를 옮겨주었다.

 

        .dog{
            position: absolute;
            left: 0;
            bottom: 0;
            width:100px;
            height: 100px;
            background-repeat: no-repeat;
            background-position: 50% 50%;
            background-size: contain;
            /* 
            이동 애니메이션을 위해 미리 이동시켜놓기
            translate3D를 활용하면 GPU를 특별히 사용한다고 한다
            %를 지정하면 각 수치에 따라 알아서 움직이기때문에 편리하다.
             */
             
            transform: translate3d(100%, 0, 0);
        }

translate를 활용해서 강아지를 오른쪽으로 옮겨주고 

 

 

overflow-hidden을 통해 door-back을 벗어나는 부분들은 가려지도록 하였다.

 

  .door-back{
            overflow: hidden;
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: black;
        }

 

 

 

 

transoform: translate3d(50%, 0, 0)을 하였을때, 반만 보이게된다.

 

 

이제 문을 여는 애니메이션을 추가해준다.

 

 

 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/reset.css">
    <style>
        .stage{
            width: 100vw;
            height: 100vh;
            background: #333;
            display: flex;
            justify-content: center;
            align-items: center;
            
        }
        .door {
            position: relative;
            width: 100px;
            height: 150px;
            
            
        }
        .door-back{
            overflow: hidden;
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: black;
        }
        .dog{
            position: absolute;
            left: 0;
            bottom: 0;
            width:100px;
            height: 100px;
            background-repeat: no-repeat;
            background-position: 50% 50%;
            background-size: contain;
            /* 
            이동 애니메이션을 위해 미리 이동시켜놓기
            translate3D를 활용하면 GPU를 특별히 사용한다고 한다
            %를 지정하면 각 수치에 따라 알아서 움직이기때문에 편리하다.
             */
            
            transform: translate3d(100%, 0, 0);
        }
        .door:nth-child(1) .dog{
            background-image: url("images/dog1.png");
        }
        .door:nth-child(2) .dog{
            background-image: url("images/dog2.png");
        }
        .door:nth-child(3) .dog{
            background-image: url("images/dog3.png");
        }

        .door-body{
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            transition: 1s;
            transform-origin: 0%;
        }
        .door:nth-child(1) .door-body{
            background: rgba(255, 0, 0, 0.7);
        }
        .door:nth-child(2) .door-body{
            background: rgba(0, 255, 0, 0.7);
        }
        .door:nth-child(3) .door-body{
            background: rgba(0, 0, 255, 0.7);
        }
        .door:hover .door-body{
            transform: perspective(800px) rotateY(-120deg);
  
        }
     </style>
</head>
<body>
    <div class="stage">
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>         
            <div class="door-body"></div>
        </div>
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>         
            <div class="door-body"></div>
        </div>
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>     
            <div class="door-body"></div>
        </div>
    </div>
    
</body>
</html>

 

클릭 이벤트를 주기 전 

 rotateY 속성을 활용해서 y축으로 회전을 시키고 perspective를 활용해서 입체감이 있게 만들었다.

 

 

 

 

 

이제 dog부분이 다시 나타날 수 있도록 정해준다. 

 

hover시 translate를 0으로 돌아오게하고 

.door:hover .dog{
            transform: translate3d(0, 0, 0);
        }

.dog에 transition을 추가해주었다.

 

.dog{
            position: absolute;
            left: 0;
            bottom: 0;
            width:100px;
            height: 100px;
            background-repeat: no-repeat;
            background-position: 50% 50%;
            background-size: contain;
            /* 
            이동 애니메이션을 위해 미리 이동시켜놓기
            translate3D를 활용하면 GPU를 특별히 사용한다고 한다
            %를 지정하면 각 수치에 따라 알아서 움직이기때문에 편리하다.
             */         
            transform: translate3d(100%, 0, 0);
            transition: 1s 1s;
        }

 

 

 

 

 

이제 스크립트 부분을 추가해서 click이벤트로 지정하기 위해

hover 부분을 제거하고 door-opened로 변경해준다.

 

 .door-opened .door-body{
            transform: perspective(800px) rotateY(-120deg);
        }
 .door-opened .dog{
            transform: translate3d(0, 0, 0);
        }

 

 

이제 짜야할 로직은 click 시에 해당 문을 열었다가 닫는 로직을 작성해주면된다.

 

이벤트 위임을 활용해서 click 이벤트를 줘봤다.

 

또한 3초가 지나면 해당 class가 사라지도록 진행해주었다. 

 

 <script>
        (function(){
            const stage = document.querySelector(".stage");
            function doorHandler(e){
                // 부모클래스를 타고 가도록 진행하기
                let element = e.target;
                
                element.classList.add('door-opened');
                setTimeout(() => {
                    element.classList.remove('door-opened');
                }, 3000)
            }

            stage.addEventListener('click', doorHandler)
        })();
    </script>

 

당연히 door이 클릭이 될 줄 알았지만 그게 아닌 door-body에 클릭이 진행이 된다. 

 

 

 

 

따라서 부모 태그를 타고 가서 해당 태그가 door일때 해당 클래스를 추가 할 수 있도록  해준다..

 

<script>
        (function(){
            const stage = document.querySelector(".stage");
            function doorHandler(e){
                // 부모클래스를 타고 가도록 진행하기
                let element = e.target;
                while(!element.classList.contains('door')){
                    element = element.parentNode;
                    if(element.nodeName === 'BODY'){
                        element = null;
                        return;
                    }
                }
                element.classList.add('door-opened');
             
            }

            stage.addEventListener('click', doorHandler)
        })();
    </script>

 

위의 코드를 if문을 활용해서 변경시켜보았다.

 

 

<script>
        (function(){
            const stage = document.querySelector(".stage");
            function doorHandler(e){
                if(element.classList.contains('door-body')){
                    element.parentNode.classList.add('door-opened');
                }
              
            }

            stage.addEventListener('click', doorHandler)
        })();
    </script>

 

 

이제 닫는 부분을 진행 시켜줘야한다.

닫는 부분을 진행 할 때 필요한 조건은 다른 문을 열때 열려있던 문을 닫아줘야한다. 

 

이를 위해 변수를 활용해서 값을 저장시키는 방식을 활용한다..

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/reset.css">
    <style>
        .stage{
            width: 100vw;
            height: 100vh;
            background: #333;
            display: flex;
            justify-content: center;
            align-items: center;
            
        }
        .door {
            position: relative;
            width: 100px;
            height: 150px;
            
            
        }
        .door-back{
            overflow: hidden;
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: black;
        }
        .dog{
            position: absolute;
            left: 0;
            bottom: 0;
            width:100px;
            height: 100px;
            background-repeat: no-repeat;
            background-position: 50% 50%;
            background-size: contain;
            /* 
            이동 애니메이션을 위해 미리 이동시켜놓기
            translate3D를 활용하면 GPU를 특별히 사용한다고 한다
            %를 지정하면 각 수치에 따라 알아서 움직이기때문에 편리하다.
             */         
            transform: translate3d(100%, 0, 0);
            transition: 1s 1s;

        }
        .door:nth-child(1) .dog{
            background-image: url("images/dog1.png");
        }
        .door:nth-child(2) .dog{
            background-image: url("images/dog2.png");
        }
        .door:nth-child(3) .dog{
            background-image: url("images/dog3.png");
        }

        .door-body{
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            transition: 1s;
            transform-origin: 0%;
        }
        .door:nth-child(1) .door-body{
            background: rgba(255, 0, 0, 0.7);
        }
        .door:nth-child(2) .door-body{
            background: rgba(0, 255, 0, 0.7);
        }
        .door:nth-child(3) .door-body{
            background: rgba(0, 0, 255, 0.7);
        }
        .door-opened .door-body{
            transform: perspective(800px) rotateY(-120deg);
        }
        .door-opened .dog{
            transform: translate3d(0, 0, 0);
        }
     </style>
</head>
<body>
    <div class="stage">
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>         
            <div class="door-body"></div>
        </div>
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>         
            <div class="door-body"></div>
        </div>
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>     
            <div class="door-body"></div>
        </div>
    </div>
    
    <script>
        (function(){
            const stage = document.querySelector(".stage");

            // 현재 활성화된 아이템을 저장
            let currentItem;

            function doorHandler(e){
                // 부모클래스를 타고 가도록 진행하기
                let element = e.target;
  
                if(currentItem){
                    currentItem.classList.remove('door-opened');
                }

                if(element.classList.contains('door-body')){
                    element.parentNode.classList.add('door-opened');
                    currentItem = element.parentNode;
                }
            }

            stage.addEventListener('click', doorHandler)
        })();
    </script>
</body>
</html>

currentItem을 활용해서 값을 저장해주고 클릭 이벤트 발생시에 해당 문을 닫아준다.

 

해당 부분을 리팩토링 진행해주면 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="css/reset.css">
    <style>
        .stage{
            width: 100vw;
            height: 100vh;
            background: #333;
            display: flex;
            justify-content: center;
            align-items: center;
            
        }
        .door {
            position: relative;
            width: 100px;
            height: 150px;
            
            
        }
        .door-back{
            overflow: hidden;
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: black;
        }
        .dog{
            position: absolute;
            left: 0;
            bottom: 0;
            width:100px;
            height: 100px;
            background-repeat: no-repeat;
            background-position: 50% 50%;
            background-size: contain;
            /* 
            이동 애니메이션을 위해 미리 이동시켜놓기
            translate3D를 활용하면 GPU를 특별히 사용한다고 한다
            %를 지정하면 각 수치에 따라 알아서 움직이기때문에 편리하다.
             */         
            transform: translate3d(100%, 0, 0);
            transition: 1s 1s;

        }
        .door:nth-child(1) .dog{
            background-image: url("images/dog1.png");
        }
        .door:nth-child(2) .dog{
            background-image: url("images/dog2.png");
        }
        .door:nth-child(3) .dog{
            background-image: url("images/dog3.png");
        }

        .door-body{
            position: absolute;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            transition: 1s;
            transform-origin: 0%;
        }
        .door:nth-child(1) .door-body{
            background: rgba(255, 0, 0, 0.7);
        }
        .door:nth-child(2) .door-body{
            background: rgba(0, 255, 0, 0.7);
        }
        .door:nth-child(3) .door-body{
            background: rgba(0, 0, 255, 0.7);
        }
        .door-opened .door-body{
            transform: perspective(800px) rotateY(-120deg);
        }
        .door-opened .dog{
            transform: translate3d(0, 0, 0);
        }
     </style>
</head>
<body>
    <div class="stage">
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>         
            <div class="door-body"></div>
        </div>
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>         
            <div class="door-body"></div>
        </div>
        <div class="door">
            <div class="door-back">
                <div class="dog"></div>
            </div>     
            <div class="door-body"></div>
        </div>
    </div>
    
    <script>
        (function(){
            const stage = document.querySelector(".stage");

            // 현재 활성화된 아이템을 저장
            let currentItem;

            function activate(item){
                item.classList.add('door-opened');
                currentItem = item;
            }

            function inactivate(item){
                item.classList.remove('door-opened');
            }

            function doorHandler(e){
                // 부모클래스를 타고 가도록 진행하기
                let element = e.target;
                if(currentItem){
                    inactivate(currentItem);
                }
                if(element.classList.contains('door-body')){
                   activate(element.parentNode);
                }
            }

            stage.addEventListener('click', doorHandler);

            activate(document.querySelector('.door:first-child'))
        })();
    </script>
</body>
</html>

 

 

 

 

움직이는 문이 완성되었다.

 

 

 

www.inflearn.com/course/interactive_web/dashboard

위의 강의를 듣고 정리한 글입니다.