본문 바로가기
이펙트 만들기/패럴랙스 이펙트 만들기

페럴렉스 이팩트 02

by 코딩달림 2022. 9. 14.
728x90

페럴랙스 이펙트

자바스크립트를 이용한 페럴랙스 효과 만들기 입니다.


페럴랙스 이펙트 02 - 사이드 메뉴

패럴랙스 01과 동일한 효과이지만 매뉴를 사이드에 적용하는 방법입니다.

▶ 화면 구성 형태

이번 효과는 패럴랙스 이펙트 01의 메뉴바를 사이드로 만들고 CSS를 이용해 지정된 부분을 강조하는 원을 그려야 합니다.

(사이드 이미지)
//HTML 파트
<nav id="parallax__dot">
    <ul>
        <li class="active"><a href="#section1"><span>메뉴1</span></a></li>
        <li><a href="#section2"><span>메뉴2</span></a></li>
        <li><a href="#section3"><span>메뉴3</span></a></li>
        <li><a href="#section4"><span>메뉴4</span></a></li>
        <li><a href="#section5"><span>메뉴5</span></a></li>
        <li><a href="#section6"><span>메뉴6</span></a></li>
        <li><a href="#section7"><span>메뉴7</span></a></li>
        <li><a href="#section8"><span>메뉴8</span></a></li>
        <li><a href="#section9"><span>메뉴9</span></a></li>
    </ul>
</nav>

사이드 메뉴는 패럴랙스 이펙트 01의 메뉴와 만드는 방법이 동일합니다.
하지만 텍스트를 가려야 하기 때문에 일단 <span> 안에 텍스를 넣었습니다.

//CSS 파트
<style>
    #parallax__dot {
        position: fixed;
        right: 20px;
        top: 50%;
        transform: translateY(-50%);
        z-index: 2000;
        background: rgba(0,0,0,0.4);
        padding: 10px 20px;
        border-radius: 30px;
        z-index: 10000;
    }
    #parallax__dot li {
        position: relative;
        width: 20px;
        height: 20px;
        border-radius: 50%;
        margin: 12px 10px;
        box-shadow: 0 0 0 2px rgba(255,255,255,1);
        transition: box-shadow .2s ease
    }
    #parallax__dot li a {
        background: #fff;
        width: 100%;
        height: 100%;
        position: absolute;
        left: 0;
        top: 0;
        border-radius: 50%;
        transition: transform .2s ease
    }
    #parallax__dot li.active {
        box-shadow: 0 0 0 2px rgba(255,255,255,1);
    }
    #parallax__dot li.active a {
        transform: scale(0.4);
    }
    #parallax__dot li span {
        display: none;
    }
</style>

CSS로 <li>와 <a> 태그에 동일한 크기의 원을 그리고 <a> 태그에 position: absolute를 적용해 겹치게 합니다. 이 때 모습은 원이 겹쳐있어 하나의 원처럼 보입니다.

이제 각 원에 클래스(active)를 주어 활성화 된 <a> 태그의 원이 줄어들어 강조되도록 합니다.
활성화 될 때 transition 효과를 주어 자연스럽게 바뀌도록 했습니다.

▶ JAVASCRIPT 파트

Step1. 클릭시 화면 이동
<script>
    document.querySelectorAll("#parallax__dot a").forEach(el => {
        el.addEventListener("click", e => {
            e.preventDefault();

            document.querySelector(el.getAttribute("href")).scrollIntoView({behavior: "smooth"});
        });
    });
</script>

쿼리셀렉터로 원(parallax__dot)을 선택하고 이벤트 메서드로 클릭시 해당 이미지 위치로 이동하도록 합니다.

메뉴는 태그로 되어있어 링크가 활성화되기 때문에 preventDefault( ) 메서드를 사용해 효과가 발생하지 않도록 합니다.

선택이 화면을 이동하도록 하는 메서드는 scrollIntoView( ) 입니다. 쿼리셀렉터로 이동할 이미지를 선택 후 화면 이동 메서드를 사용해 이동시킵니다.

※ 스크롤 동적 처리 속성

window.pageYOffset
window.scrollY
document.documentElement.scrollTop

위의 세가지 속성은 웹문서가 수직으로 얼마나 스크롤됐는지 픽셀 단위로 반환합니다.

하지만 각각 특정 브라우저에서 동작이 안할 수 있어 3가지를 동시에 쓰는데,
"window.pageYOffset || window.scrollY || document.documentElement.scrollTop" 형태로 사용할 수 있습니다.

Step2. 이미지 좌표값 표현

    const scroll = document.querySelector("#parallex__info .scroll span");
    window.addEventListener("scroll", () => {
        let scrollTop = window.pageYOffset || window.scrollY || document.documentElement.scrollTop;     //이런식으로 3가지를 다 써서 모든 브라우저에 적용되게 함 
    
        // info
        document.querySelector(".scroll span").innerText = Math.round(scrollTop);   //Math.round() : 값을 반올림 해줌
        
        // document.querySelector(".offset1").innerText = document.getElementById("section1").offsetTop;
        // document.querySelector(".offset2").innerText = document.getElementById("section2").offsetTop;
        // document.querySelector(".offset3").innerText = document.getElementById("section3").offsetTop;
        // document.querySelector(".offset4").innerText = document.getElementById("section4").offsetTop;
        // document.querySelector(".offset5").innerText = document.getElementById("section5").offsetTop;
        // document.querySelector(".offset6").innerText = document.getElementById("section6").offsetTop;
        // document.querySelector(".offset7").innerText = document.getElementById("section7").offsetTop;
        // document.querySelector(".offset8").innerText = document.getElementById("section8").offsetTop;
        // document.querySelector(".offset9").innerText = document.getElementById("section9").offsetTop;

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

스크립트를 통해 좌표값이 표시될 aside 구역에 각 이미지에 부여한 아이디 값을 넣고 offsetTop으로 각 이미지의 좌표값을 표시합니다.

offsetTop은 요소의 Y축 좌표값을 표현해주며, offsetLeft는 X축 좌표값을 나타냅니다. 이 값은 문서를 기준으로 합니다.

9개의 이미지가 반복되어야 하기에 for문으로 처리하였습니다.

※ 수학 메서드

Math.round( ) : 요소의 값을 반올림 해주는 메서드
Math.floor( ) : 요소의 값을 버림 해주는 메서드
Math.ceil( ) : 요소의 값을 올림 해주는 메서드
Math.max( ) : 요소의 가장 큰 값을 나타내는 메서드
Math.min( ) : 요소의 가장 작은 값을 나타내는 메서드
Math.abs( ) : 요소의 절대값을 나타내는 메서드
Math.trulc( ) : 요소의 정수값만 나타내는 메서드
Math.random( ) : 요소의 랜덤 값(0-1사이의 난수)을 나타내는 메서드

Step3. 메뉴와 이미지 매칭
window.addEventListener("scroll", () => {
    let scrollTop = window.pageYOffset || window.scrollY || document.documentElement.scrollTop;     //이런식으로 3가지를 다 써서 모든 브라우저에 적용되게 함 

    // if(scrollTop >= document.getElementById("section1").offsetTop){
    //     document.querySelectorAll("#parallax__nav li").forEach(li => {
    //         li.classList.remove("active");
    //     });
    //     document.querySelector("#parallax__nav li:nth-child(1)").classList.add("active");
    // };
    // if(scrollTop >= document.getElementById("section2").offsetTop){
    //     document.querySelectorAll("#parallax__nav li").forEach(li => {
    //         li.classList.remove("active");
    //     });
    //     document.querySelector("#parallax__nav li:nth-child(2)").classList.add("active");
    // };
    // if(scrollTop >= document.getElementById("section3").offsetTop){
    //     document.querySelectorAll("#parallax__nav li").forEach(li => {
    //         li.classList.remove("active");
    //     });
    //     document.querySelector("#parallax__nav li:nth-child(3)").classList.add("active");
    // };
    // if(scrollTop >= document.getElementById("section4").offsetTop){
    //     document.querySelectorAll("#parallax__nav li").forEach(li => {
    //         li.classList.remove("active");
    //     });
    //     document.querySelector("#parallax__nav li:nth-child(4)").classList.add("active");
    // };
    // if(scrollTop >= document.getElementById("section5").offsetTop){
    //     document.querySelectorAll("#parallax__nav li").forEach(li => {
    //         li.classList.remove("active");
    //     });
    //     document.querySelector("#parallax__nav li:nth-child(5)").classList.add("active");
    // };
    // if(scrollTop >= document.getElementById("section6").offsetTop){
    //     document.querySelectorAll("#parallax__nav li").forEach(li => {
    //         li.classList.remove("active");
    //     });
    //     document.querySelector("#parallax__nav li:nth-child(6)").classList.add("active");
    // };
    // if(scrollTop >= document.getElementById("section7").offsetTop){
    //     document.querySelectorAll("#parallax__nav li").forEach(li => {
    //         li.classList.remove("active");
    //     });
    //     document.querySelector("#parallax__nav li:nth-child(7)").classList.add("active");
    // };
    // if(scrollTop >= document.getElementById("section8").offsetTop){
    //     document.querySelectorAll("#parallax__nav li").forEach(li => {
    //         li.classList.remove("active");
    //     });
    //     document.querySelector("#parallax__nav li:nth-child(8)").classList.add("active");
    // };
    // if(scrollTop >= document.getElementById("section9").offsetTop){
    //     document.querySelectorAll("#parallax__nav li").forEach(li => {
    //         li.classList.remove("active");
    //     });
    //     document.querySelector("#parallax__nav li:nth-child(9)").classList.add("active");
    // };


    //for
    // for(let i=1; i<=9; i++){
    //     if(scrollTop >= document.getElementById("section"+i).offsetTop){
    //         document.querySelectorAll("#parallax__nav li").forEach(li => {
    //             li.classList.remove("active");
    //         });
    //         document.querySelector("#parallax__nav li:nth-child("+i+")").classList.add("active");
    //     };
    // }
    //forEach
    document.querySelectorAll(".content__item").forEach((element, index) =>{
        if(scrollTop >= element.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");
        };
    });
    // info
    document.querySelector(".scroll span").innerText = Math.round(scrollTop);   //Math.round() : 값을 반올림 해줌

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

if문을 이용해 스크롤 위치값이 이미지의 위치값과 크거나 같을 때, 즉 스크롤이 이미지 위치로 이동했을 때 이미지에 맞는 해당 메뉴에만 효과를 주는 방법입니다.

스크롤이 각 이미지에 도달했을 때 클래스를 부여해 효과를 발동시키는데, 한번 지날 때 부여된게 지속되기 때문에 각 이미지를 지나갈 때 전체 이미지의 효과를 주는 클래스를 제거하고 그 다음 해당 이미지에만 클래스를 부여해 해당 메뉴에만 효과가 부여하도록 합니다.

이 작업을 이미지 수 만큼 반복해야 하기에 for문 혹은 forEach문으로 반복문을 처리하면 코드를 간단하게 줄일 수 있습니다.

Step4. 메뉴 클릭 시 부드럽게 이동하는 효과
window.addEventListener("scroll", () => {
    let scrollTop = window.pageYOffset || window.scrollY || document.documentElement.scrollTop;     //이런식으로 3가지를 다 써서 모든 브라우저에 적용되게 함 

    document.querySelectorAll(".content__item").forEach((element, index) =>{
        if(scrollTop >= element.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");
        };
    });

    // info
    document.querySelector(".scroll span").innerText = Math.round(scrollTop);   //Math.round() : 값을 반올림 해줌

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

//스크롤 이동
document.querySelectorAll("#parallax__nav li a").forEach(li => {
    li.addEventListener("click", (e) => {
        e.preventDefault();
        document.querySelector(li.getAttribute("href")).scrollIntoView({
            behavior: "smooth"  //부드럽게 움직이도록 해줌
        });
    });
});

메뉴를 클릭 시 해당 위치로 이동하는 방법은 html의 a태그 href를 사용해 구현할 수 있습니다. 하지만 이런 방식은 곧바로 이동할 뿐 애니메이션을 주듯 부드러운 움직임을 구현하지 않습니다.
그래서 스크립트를 통해 움직임을 구현합니다.

※ scrollIntoView( )

element.scrollIntoView( ) : 특정 요소 위치로 화면 스크롤을 이동시키는 메서드
- behavior : 전환 애니메이션을 정의 (smooth : 부드럽게 이동)

scrollIntoView( )는 스크롤의 위치를 이동시키는 메서드 입니다. 이 때 몇가지 효과를 줄 수 있는데 "behavior: smooth"를 사용 시 스크롤이 위아래로 부드럽게 이동하게 됩니다.

※ preventDefault( )

event.preventDefault( ) : 특정 이벤트의 발생을 막아주는 메서드

preventDefault( )는 <a> 태그나 <submit> 태그 같은 몇몇 특정 기능을 갖는 태그의 효과를 비활성화 시켜줍니다.
본문에선 메뉴에 <a> 태그가 주어져 있으므로 스크립트와 충돌이 나지 않게 preventDefault( ) 메서드로 비활성화 했습니다.

'이펙트 만들기 > 패럴랙스 이펙트 만들기' 카테고리의 다른 글

패럴랙스 이펙트 06  (2) 2022.09.30
패럴랙스 이펙트 04  (1) 2022.09.26
패럴랙스 이펙트 03  (2) 2022.09.24
패럴랙스 이펙트 05  (3) 2022.09.21
패럴랙스 이펙트 만들기 01  (5) 2022.09.06

댓글


광고 준비중입니다.