연습문제

- 다음과 같은 HTML 문서와 JavaScript 프로그램을 작성하라.

  • 화면이 특정 위치까지 스크롤 되면 직사각형의 각 변을 그리는 기능

 

풀이

1. 브라우저가 스크롤 되면

    1.1. #box 요소의 테두리가 그려져 있지 않고 지정한 위치보다 아래로 스크롤 되면

    1.2. #box 요소의 테두리를 그린다.

    1.3. #box 요소의 테두리가 그려져 있고 지정한 위치보다 위로 스크롤 되면

    1.4. #box 요소의 테두리를 제거한다.

 

 

 

 

● HTML

<body>
    <div class="page"><h2>READY!</h2></div>
    <div class="page">
        <div id="box">
            <span class="border"></span>
            <span class="border"></span>
            <span class="border"></span>
            <span class="border"></span>
        </div>
    </div>
    <div class="page"><h2>DONE!</h2></div>
</body>

 

 

● CSS

<style>
    html, body {height: 100%;}
    
    .page {
        /* 요소의 높이를 뷰포트의 높이만큼 설정 */
        height: 100%;

        /* h2 요소와 #box 요소가 .page 요소의 영역을 기준으로 배치되도록 */
        position: relative;
    }

    .page > h2 {
        /* .page 요소의 영역을 기준으로 중앙에 배치 */
        position: absolute;
        top: 50%; left: 50%;
        transform: translate(-50%, -50%);

        font-size: 10em;
        font-weight: 900;
        letter-spacing: -2px;
    }

    #box {
        width: 600px;
        height: 600px;

        /* .page 요소의 영역을 기준으로 중앙에 배치 */
        position: absolute;
        top: 50%; left: 50%;
        transform: translate(-50%, -50%);
    }

    #box > .border {
        /* #box 요소의 영역을 기준으로 각각 위쪽, 오른쪽, 아래쪽, 왼쪽에 배치 */
        position: absolute;
        background-color: black;
    }

    /* 첫 번째 테두리와 세 번째 테두리는 가로 테두리를 표현 */
    #box > .border:nth-child(odd) {
        width: 0;
        height: 30px;
    }

    /* 두 번째 테두리와 네 번째 테두리는 세로 테두리를 표현 */
    #box > .border:nth-child(even) {
        width: 30px;
        height: 0;
    }

    /* 첫 번째 테두리를 #box 요소의 좌상단을 기준으로 배치 */
    #box > .border:nth-child(1) {top: 0; left: 0;}

    /* 두 번째 테두리를 #box 요소의 우상단을 기준으로 배치 */
    #box > .border:nth-child(2) {top: 0; right: 0;}

    /* 세 번째 테두리를 #box 요소의 우하단을 기준으로 배치 */
    #box > .border:nth-child(3) {bottom: 0; right: 0;}

    /* 세 번째 테두리를 #box 요소의 좌하단을 기준으로 배치 */
    #box > .border:nth-child(4) {bottom: 0; left: 0;}
    
</style>

 

 

● script

<script src="https://code.jquery.com/jquery.min.js"></script>
<script>
    $(function () {
        // 이벤트 핸들러: document 객체에서 ready 이벤트가 발생하면 실행할 기능
        // → HTML 문서를 읽어들인 후 해석하고 DOM 구축을 끝낸 시점에 발생하는 이벤트
        // → window 객체의 load 이벤트와 비슷한 시점에 발생하는 이벤트
        //----------------------------------------------------------------------
        // 프로그램에서 참조하는 요소를 미리 탐색
        var $box = $("#box");

        // #box 요소의 테두리를 그리거나 제거할 기준 위치 설정
        var posTop = $box.offset().top - 100;

        // #box 요소의 테두리를 그렸는 지 나타내는 변수
        // 위와 같은 변수를 쓰지 않고 조건문을 작성한다면 의도한 바대로 효과가 나타나지 않는다.
        var boxDrawn = false;

        // #box 요소의 테두리를 그리는 중인지 나타내는 변수
        // 위와 같은 변수를 쓰는 이유는 테두리를 그리는 애니메이션 효과가 발생 중일 때 스크롤 이벤트가
        // 발생한다 하더라도 애니메이션 큐에 쌓이게 하지 않고 무시하도록 하기 위해서이다.
        var boxDrawing = false;

        console.log("$box.offset().top = " + $box.offset().top);

    
        // 1. 브라우저가 스크롤 되면
        $(window).on("scroll", function () {

            // #box 요소의 테두리를 그리거나 제거하는 사이에 scroll 이벤트가 발생하면 무시한다.
            if (boxDrawing) return;

            // 1.1. #box 요소의 테두리가 그려져 있지 않고 지정한 위치보다 아래로 스크롤 되면
            if (!boxDrawn && $(window).scrollTop() > posTop) {

                // #box 요소의 테두리를 그리기 시작했음을 표시
                boxDrawing = true;
                
                // 1.2. #box 요소의 테두리를 그린다.
                $box.children().eq(0).animate({"width": "100%"}, 500);
                $box.children().eq(1).delay(500).animate({"height": "100%"}, 500);
                $box.children().eq(2).delay(1000).animate({"width": "100%"}, 500);
                $box.children().eq(3).delay(1500).animate({"height": "100%"}, 500, function() {
                    // 네 번째 테두리를 다 그린 다음 #box 요소의 테두리를 모두 그렸음을 표시
                    boxDrawn = true;
                    boxDrawing = false;
                });
            }
            // 1.3. #box 요소의 테두리가 그려져 있고 지정한 위치보다 위로 스크롤 되면
            else if (boxDrawn && $(window).scrollTop() < posTop) {

                // #box 요소의 테두리를 제거하기 시작했음을 표시
                boxDrawing = true;

                // 1.4. #box 요소의 테두리를 제거한다.
                $box.children().eq(3).animate({"height": 0}, 500);
                $box.children().eq(2).delay(500).animate({"width": 0}, 500);
                $box.children().eq(1).delay(1000).animate({"height": 0}, 500);
                $box.children().eq(0).delay(1500).animate({"width": 0}, 500, function () {
                    // 첫 번째 테두리를 제거한 다음 #box 요소의 테두리를 모두 제거했음을 표시
                    boxDrawn = false;
                    boxDrawing = false;
                });
            }
        }); // window.onscroll
    }); // document.onready

</script>