JAVASCRIPT

패럴랙스 (parallax) 이펙트 ( 1 ) pageYOffset,scrollY,scrollTop

김도현2 2023. 4. 18. 18:35
반응형

패럴랙스 (parallax) 이펙트 ( 1 ) pageYOffset,scrollY,scrollTop

패럴랙스 이펙트를 이용해 사이트 스크롤에 효과를 줬습니다.

 

 

 

 

 

 

 

 

 

VSCode

<!DOCTYPE html>
<html lang="ko">
<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>패럴랙스 이펙트01</title>

    <link rel="stylesheet" href="css/reset.css">
    <link rel="stylesheet" href="css/parallax.css">
</head>
<body class="bg01 font01">
    <header id="header">
        <h1>Javascript parallax Effect01</h1>
        <p>패럴랙스 이펙트 : 메뉴 효과</p>
        <ul>
            <li class="active"><a href="parallax01.html">1</a></li>
            <li><a href="parallax02.html">2</a></li>
            <li><a href="parallax03.html">3</a></li>
            <li><a href="parallax04.html">4</a></li>
            <li><a href="parallax05.html">5</a></li>
            <li><a href="parallax06.html">6</a></li>
            <li><a href="parallax07.html">7</a></li>
        </ul>
    </header>
    <!-- //header-->

    <nav class="parallax__nav">
        <ul>
            <li class="active"><a href="#section1">메뉴1</a></li>
            <li><a href="#section2">메뉴2</a></li>
            <li><a href="#section3">메뉴3</a></li>
            <li><a href="#section4">메뉴4</a></li>
            <li><a href="#section5">메뉴5</a></li>
            <li><a href="#section6">메뉴6</a></li>
            <li><a href="#section7">메뉴7</a></li>
            <li><a href="#section8">메뉴8</a></li>
            <li><a href="#section9">메뉴9</a></li>
        </ul>
    </nav>

    <main id="main">
        <div id="parallax__wrap">
            <section id="section1" class="parallax__item">
                <span class="parallax__item__num">01</span>
                <h2 class="parallax__item__title">Section1</h2>
                <figure class="parallax__item__imgWrap">
                    <div class="parallax__item__img"></div>
                </figure>
                <p class="parallax__item__desc">행운의 여신은 용기 있는 자를 좋아한다.</p>
            </section>
            <!-- //section1 -->

            <section id="section2" class="parallax__item">
                <span class="parallax__item__num">02</span>
                <h2 class="parallax__item__title">Section2</h2>
                <figure class="parallax__item__imgWrap">
                    <div class="parallax__item__img"></div>
                </figure>
                <p class="parallax__item__desc">꿈이 없다면 아무 일도 일어나지 않는다.</p>
            </section>
            <!-- //section2 -->

            <section id="section3" class="parallax__item">
                <span class="parallax__item__num">03</span>
                <h2 class="parallax__item__title">Section3</h2>
                <figure class="parallax__item__imgWrap">
                    <div class="parallax__item__img"></div>
                </figure>
                <p class="parallax__item__desc">지나간 일로 미래를 설계할 수는 없다.</p>
            </section>
            <!-- //section3 -->

            <section id="section4" class="parallax__item">
                <span class="parallax__item__num">04</span>
                <h2 class="parallax__item__title">Section4</h2>
                <figure class="parallax__item__imgWrap">
                    <div class="parallax__item__img"></div>
                </figure>
                <p class="parallax__item__desc">준비하지 않은 자는 기회가 와도 소용없다.</p>
            </section>
            <!-- //section4 -->
            
            <section id="section5" class="parallax__item">
                <span class="parallax__item__num">05</span>
                <h2 class="parallax__item__title">Section5</h2>
                <figure class="parallax__item__imgWrap">
                    <div class="parallax__item__img"></div>
                </figure>
                <p class="parallax__item__desc">노력에 집착하라. 숙명적인 노력을.</p>
            </section>
            <!-- //section5 -->
            
            <section id="section6" class="parallax__item">
                <span class="parallax__item__num">06</span>
                <h2 class="parallax__item__title">Section6</h2>
                <figure class="parallax__item__imgWrap">
                    <div class="parallax__item__img"></div>
                </figure>
                <p class="parallax__item__desc">내일이란 오늘의 다른 이름일 뿐</p>
            </section>
            <!-- //section6 -->
            
            <section id="section7" class="parallax__item">
                <span class="parallax__item__num">07</span>
                <h2 class="parallax__item__title">Section7</h2>
                <figure class="parallax__item__imgWrap">
                    <div class="parallax__item__img"></div>
                </figure>
                <p class="parallax__item__desc">불가능한 일을 해보는 것은 신나는 일이다.</p>
            </section>
            <!-- //section7 -->
            
            <section id="section8" class="parallax__item">
                <span class="parallax__item__num">08</span>
                <h2 class="parallax__item__title">Section8</h2>
                <figure class="parallax__item__imgWrap">
                    <div class="parallax__item__img"></div>
                </figure>
                <p class="parallax__item__desc">당신이 포기할 때, 나는 시작한다.</p>
            </section>
            <!-- //section8 -->
            
            <section id="section9" class="parallax__item">
                <span class="parallax__item__num">09</span>
                <h2 class="parallax__item__title">Section9</h2>
                <figure class="parallax__item__imgWrap">
                    <div class="parallax__item__img"></div>
                </figure>
                <p class="parallax__item__desc">나이가 성숙을 보장하지는 않는다.</p>
            </section>
            <!-- //section9 -->
            
            
        </div>
    </main>
    <!-- //main -->

    <aside class="parallax_info">
        <div class="scroll">scrollTop : <span>0</span>px</div>
        <div class="info">
            <ul>
                <li>#section1 offset() : <span class="offset1">0</span>px</li>
                <li>#section2 offset() : <span class="offset2">0</span>px</li>
                <li>#section3 offset() : <span class="offset3">0</span>px</li>
                <li>#section4 offset() : <span class="offset4">0</span>px</li>
                <li>#section5 offset() : <span class="offset5">0</span>px</li>
                <li>#section6 offset() : <span class="offset6">0</span>px</li>
                <li>#section7 offset() : <span class="offset7">0</span>px</li>
                <li>#section8 offset() : <span class="offset8">0</span>px</li>
                <li>#section9 offset() : <span class="offset9">0</span>px</li>
            </ul>
        </div>
    </aside>

    <footer id="footer">
        <a href="mailto:ehcjswo1@gmail.com">ehcjswo1@gmail.com</a>
    </footer>
    <!-- //footer-->

    <script>
        window.addEventListener("scroll", () => {
            // 현재 스크롤 위치
            let scrollTop = window.pageYOffset || window.scrollY || document.documentElement.scrollTop;

            // 선택자
            const info = document.querySelectorAll(".info ul li span");

            
            document.querySelectorAll(".parallax__item").forEach((item, index)=>{
                if(scrollTop >= item.offsetTop-2){
                    document.querySelectorAll(".parallax__nav li").forEach((li) => {
                        li.classList.remove("active")
                    });
                    document.querySelector(".parallax__nav li:nth-child("+(index+1)+")").classList.add("active");
                }
            });

            document.querySelectorAll(".parallax__nav li a").forEach(li => {
                li.addEventListener("click", (e) => {
                    e.preventDefault();
                    document.querySelector(li.getAttribute("href")).scrollIntoView({
                        behavior: "smooth"
                    });
                })
            })

            //info
            document.querySelector(".scroll span").innerText = parseInt(scrollTop);

            document.querySelector(".info .offset1").innerText = document.getElementById("section1").offsetTop;
            document.querySelector(".info .offset2").innerText = document.getElementById("section2").offsetTop;
            document.querySelector(".info .offset3").innerText = document.getElementById("section3").offsetTop;
            document.querySelector(".info .offset4").innerText = document.getElementById("section4").offsetTop;
            document.querySelector(".info .offset5").innerText = document.getElementById("section5").offsetTop;
            document.querySelector(".info .offset6").innerText = document.getElementById("section6").offsetTop;
            document.querySelector(".info .offset7").innerText = document.getElementById("section7").offsetTop;
            document.querySelector(".info .offset8").innerText = document.getElementById("section8").offsetTop;
            document.querySelector(".info .offset9").innerText = document.getElementById("section9").offsetTop;
            
            // for문
            // for(let i=1; i<=9; i++){
            //     document.querySelector(".info .offset"+i).innerText = document.getElementById("section"+i).offsetTop;
            // };

            //forEach문
            // info.forEach((el,i)=> {
            //     el.innerText = document.getElementById("section"+(i+1)).offsetTop
            // });
            
            //for of문
            // for (const [i, el] of info.entries()) {
            //     el.innerText = document.getElementById(`section${i+1}`).offsetTop;
            // }

            //for in문
            // for (const i in info) {
            //     info[i].innerText = document.getElementById(`section${parseInt(i)+1}`).offsetTop;
            // }
        });



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

 

navigation bar는 "parallax__nav" 클래스로 정의되어 있으며, "ul" 태그 내에 "li" 태그를 이용해 각 메뉴를 구성하고 있습니다. 

 

"a" 태그를 이용하여 각 메뉴를 클릭하면 해당 section으로 이동하게 됩니다. 각 section은 "parallax__item" 클래스로 정의되어 있으며, "section" 태그를 이용하여 각각의 section을 구성하고 있습니다. 각 section은 "id" 속성을 이용하여 고유하게 식별되어 있으며, navigation bar에서 해당 section으로 이동하기 위해 사용되고 있습니다. 

 

각 section은 "span" 태그를 이용하여 번호를 나타내고 있으며, "h2" 태그와 "p" 태그를 이용하여 section의 제목과 설명을 나타내고 있습니다. 또한 "figure" 태그와 "div" 태그를 이용하여 이미지를 나타내고 있습니다. 전반적으로 이 코드는 한 페이지에서 여러 섹션들을 parallax scrolling 기법을 이용하여 구성하는 코드입니다.

 

 

 

 

script

window.addEventListener("scroll", () => {
    // 현재 스크롤 위치
    let scrollTop = window.pageYOffset || window.scrollY || document.documentElement.scrollTop;

    // 선택자
    const info = document.querySelectorAll(".info ul li span");


    document.querySelectorAll(".parallax__item").forEach((item, index)=>{
        if(scrollTop >= item.offsetTop-2){
            document.querySelectorAll(".parallax__nav li").forEach((li) => {
                li.classList.remove("active")
            });
            document.querySelector(".parallax__nav li:nth-child("+(index+1)+")").classList.add("active");
        }
    });

    document.querySelectorAll(".parallax__nav li a").forEach(li => {
        li.addEventListener("click", (e) => {
            e.preventDefault();
            document.querySelector(li.getAttribute("href")).scrollIntoView({
                behavior: "smooth"
            });
        });
    });

    //info
    document.querySelector(".scroll span").innerText = parseInt(scrollTop);

    document.querySelector(".info .offset1").innerText = document.getElementById("section1").offsetTop;
    document.querySelector(".info .offset2").innerText = document.getElementById("section2").offsetTop;
    document.querySelector(".info .offset3").innerText = document.getElementById("section3").offsetTop;
    document.querySelector(".info .offset4").innerText = document.getElementById("section4").offsetTop;
    document.querySelector(".info .offset5").innerText = document.getElementById("section5").offsetTop;
    document.querySelector(".info .offset6").innerText = document.getElementById("section6").offsetTop;
    document.querySelector(".info .offset7").innerText = document.getElementById("section7").offsetTop;
    document.querySelector(".info .offset8").innerText = document.getElementById("section8").offsetTop;
    document.querySelector(".info .offset9").innerText = document.getElementById("section9").offsetTop;
});

window.addEventListener("scroll", () => { ... } 부분은 스크롤이 일어날 때 실행될 함수를 등록하는 부분입니다.

 

let scrollTop = window.pageYOffset || window.scrollY || document.documentElement.scrollTop;

는 현재 스크롤 위치를 구하는 변수를 선언하는 부분입니다. window.pageYOffset, window.scrollY, document.documentElement.scrollTop는 각각 브라우저마다 스크롤 위치를 구하는 방법이 다를 수 있으므로, 이들 중 어떤 것이든 사용 가능한 값을 scrollTop에 할당합니다.

 

document.querySelectorAll(".parallax__item").forEach((item, index) => { ... }는 .parallax__item

클래스를 가진 요소들에 대해 반복문을 돌리는 부분입니다. 각 요소들의 offsetTop 값을 구해서 현재 스크롤 위치보다 작으면, 이전 요소들은 모두 제외하고 현재 요소가 활성화된 것으로 표시하는 부분입니다.

 

document.querySelectorAll(".parallax__nav li a").forEach(li => { ... }는 .parallax__nav li a

클래스를 가진 요소들에 대해 클릭 이벤트를 등록하는 부분입니다. 클릭이 발생하면 해당 요소의 href 속성 값으로 지정된 위치로 부드러운 스크롤 이동을 수행합니다.

 

document.querySelector(".info ...").innerText

.info 클래스를 가진 요소들에 대해 스크롤 위치에 대한 정보를 업데이트하는 부분입니다. 이 부분에서는 각 섹션 요소의 offsetTop 값을 .offset1, .offset2 등의 클래스를 가진 요소에 업데이트합니다.

 

 

 

 

 

 

document.querySelector(".info .offset1").innerText = document.getElementById("section1").offsetTop;
document.querySelector(".info .offset2").innerText = document.getElementById("section2").offsetTop;
document.querySelector(".info .offset3").innerText = document.getElementById("section3").offsetTop;
document.querySelector(".info .offset4").innerText = document.getElementById("section4").offsetTop;
document.querySelector(".info .offset5").innerText = document.getElementById("section5").offsetTop;
document.querySelector(".info .offset6").innerText = document.getElementById("section6").offsetTop;
document.querySelector(".info .offset7").innerText = document.getElementById("section7").offsetTop;
document.querySelector(".info .offset8").innerText = document.getElementById("section8").offsetTop;
document.querySelector(".info .offset9").innerText = document.getElementById("section9").offsetTop;

// for문
for(let i=1; i<=9; i++){
    document.querySelector(".info .offset"+i).innerText = document.getElementById("section"+i).offsetTop;
};

//forEach문
const info = document.querySelectorAll(".info ul li span");

info.forEach((el,i)=> {
    el.innerText = document.getElementById("section"+(i+1)).offsetTop
});

//for of문
for (const [i, el] of info.entries()) {
    el.innerText = document.getElementById(`section${i+1}`).offsetTop;
}

//for in문
for (const i in info) {
    info[i].innerText = document.getElementById(`section${parseInt(i)+1}`).offsetTop;
}

 

각 사진의 위치를 표기하는 방법들 입니다. 

 

document.querySelector 를 9번 써서 출력하는 방법을 for문, forEach문, for of문, for in문을 사용해

 

코드 길이를 확 줄였습니다. 

 

for of문과 for in문은 사용하기 힘들었는데,

 

for of문은,

Array.entries() 메소드를 사용하여 인덱스와 해당 요소를 각각 iel 변수에 할당하고, 템플릿 리터럴을 사용하여 문자열을 생성합니다. forEach() 메소드와 달리 for...of문에서는 반복 동작 중에 배열의 각 요소에 직접 접근할 수 있으므로 배열 요소에 대한 추가 검사가 필요하지 않습니다.

 

for in문은,

for...in문은 객체의 속성을 순회할 때 사용됩니다. 이 경우, info 배열의 인덱스가 객체의 속성과 유사하게 취급되므로 for...in문을 사용하여 배열을 순회할 수 있습니다. 그러나 for...in문을 사용하여 배열을 순회할 때는 반드시 hasOwnProperty() 메소드를 사용하여 객체의 프로토타입 체인 상속 속성을 제외시켜야 합니다. 이 예시에서는 for...in문 대신 for...of문을 사용하는 것이 더욱 안전하고 권장됩니다.