LHJ

I'm a FE developer.

9. 스크롤 UI 만들기

25 Oct 2020 » js_interactive_web2

9. 스크롤 UI 만들기

background-attachment 속성은 편하긴하나 사파리, IE에서 심하게 떨린다.
그리고 모바일 기기에서 작동하지 않는 경우도 있다.

position: fixed로 하는 것이 훨씬 안정적이고 크로스브라우징 효과도 좋다.
아니면 모더나이즈 js로 처리해도 된다.


확실히 스크립트 정리는 애플 인터렉션 강좌 코드가 더 깔끔하다.

(function () {
    var winScrollTop;
    var section = Array.prototype.slice.call(document.querySelectorAll(".interaction_section"));
    var offsetTop = [];
    var offsetBottom = [];
    var naviListBun = Array.prototype.slice.call(document.querySelectorAll(".nav_list li a"));

    var setValue = function () {
        winScrollTop = pageYOffset;
        section.forEach(function (value, index) {
            offsetTop[index] = value.offsetTop;
            offsetBottom[index] = value.offsetTop + value.getBoundingClientRect().height;
        })
    }

    var sectionActive = function (index) {
        listActive(index);
        changeText(index);
        changeColor(index);
    }

    var listActive = function (index) {
        var list = Array.prototype.slice.call(document.querySelectorAll(".nav_list li a"));
        list.forEach(function (val, index) {
            val.classList.remove("active");
        })
        list[index].classList.add("active");
    }

    var changeText = function (index) {
        var targetText = document.querySelector(".fix_tit strong");
        var list = document.querySelectorAll(".nav_list li a span");
        var getText = list[index].innerText;

        targetText.innerText = getText;
    }

    var changeColor = function (index) {
        var targetText = document.querySelector(".fix_tit");
        if (index === 1 || index === 2) {
            targetText.classList.add("black");
        } else {
            targetText.classList.remove("black");
        }
    }

    var checkInSection = function () {
        switch (true) {
            case (winScrollTop >= offsetTop[0] && offsetBottom[0] > winScrollTop) :
                sectionActive(0);
                break
            case (winScrollTop >= offsetTop[1] && offsetBottom[1] > winScrollTop) :
                sectionActive(1);
                break
            case (winScrollTop >= offsetTop[2] && offsetBottom[2] > winScrollTop) :
                sectionActive(2);
                break
            case (winScrollTop >= offsetTop[3] && offsetBottom[3] > winScrollTop) :
                sectionActive(3);
                break
            case (winScrollTop >= offsetTop[4] && offsetBottom[4] > winScrollTop) :
                sectionActive(4);
                break
            case (winScrollTop >= offsetTop[5] && offsetBottom[5] > winScrollTop) :
                sectionActive(5);
                break
            case (winScrollTop >= offsetTop[6] && offsetBottom[6] > winScrollTop) :
                sectionActive(6);
                break
            case (winScrollTop >= offsetTop[7] && offsetBottom[7] > winScrollTop) :
                sectionActive(7);
                break
            default :
                break
        }
    }

    var currentYPosition = function () {
        // Firefox, Chrome, Opera, Safari
        if (self.pageYOffset) return self.pageYOffset;
        // Internet Explorer 6 - standards mode
        if (document.documentElement && document.documentElement.scrollTop)
            return document.documentElement.scrollTop;
        // Internet Explorer 6, 7 and 8
        if (document.body.scrollTop) return document.body.scrollTop;
        return 0;
    }

    var elmYPosition = function (eID) {
        // querySelector 는 IE8까지 지원
        // document.getElementById() 는 더 아래 버전까지 지원
        var elm = document.querySelector(eID);
        var y = elm.offsetTop;
        var node = elm;
        // position: relative 기준이 되는 부모요소로 부터의 상대적인 offsetTop값이기 때문에 아래와 같이해야된다.
        // 아아 부모에 position: relative;가 있던 없던 있으면 있는대로 아래식으로, 없으면 없는대로 body를 기준으로 잡기에 아래식으로 원하는 값을 구할 수 있다.
        // 좋은 코드네.
        // 아니면 좀 더 편하게 코드를 작성하기 위해서 getBoundingClientRect 이 메서드를 고려해보는 것도 괜찮다.
        while (node.offsetParent && node.offsetParent !== document.body) {
            node = node.offsetParent;
            y += node.offsetTop;
        }
        return y;
    }

    var smoothScroll = function (eID) {
        var startY = currentYPosition();
        var stopY = elmYPosition(eID);
        var distance = stopY > startY ? stopY - startY : startY - stopY;
        if (distance < 100) {
            scrollTo(0, stopY); return;
        }
        var speed = Math.round(distance / 100);
        if (speed >= 20) speed = 20;
        var step = Math.round(distance / 25);
        var leapY = stopY > startY ? startY + step : startY - step;
        var timer = 0;
        if (stopY > startY) {
            for ( var i=startY; i<stopY; i+=step ) {
                setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
                leapY += step; if (leapY > stopY) leapY = stopY; timer++;
            } return;
        }
        for ( var i=startY; i>stopY; i-=step ) {
            setTimeout("window.scrollTo(0, "+leapY+")", timer * speed);
            leapY -= step; if (leapY < stopY) leapY = stopY; timer++;
        }
        return false;
    }

    var init = function () {
        setValue();
        checkInSection();
    }

    addEventListener("scroll", function () {
        winScrollTop = pageYOffset;
        checkInSection();
    })

    addEventListener("resize", function () {
        setValue();
        checkInSection();
    })

    naviListBun.forEach(function (val, i) {
        val.addEventListener("click", function (e) {
            e.preventDefault();
            smoothScroll(this.hash);
        })
    })

    addEventListener("load", function () {
        init();
    })
})()