예제/페럴랙스 이펙트

페럴랙스 이펙트01 : 메뉴효과

아라라_ 2023. 4. 18. 18:23

“ 지연되는 프로젝트에 인력을 더 투입하면 오히려 더 늦어진다. ”

Frederick Philips Brooks
Mythical Man-Month 저자
728x90

HTML

<header id="header">
    <h1>Javascript parallax Effect01</h1>
    <p>페럴랙스 이펙트 : 메뉴효과</p>
    <ul>
        <li><a href="parallaxEffect01.html">1</a></li>
        <li><a href="parallaxEffect02.html">2</a></li>
        <li><a href="parallaxEffect03.html">3</a></li>
        <li><a href="parallaxEffect04.html">4</a></li>
        <li><a href="parallaxEffect05.html">5</a></li>
        <li class="active"><a href="parallaxEffect06.html">6</a></li>
        <li><a href="parallaxEffect07.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">sectin1</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">sectin2</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">sectin</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">sectin4</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">sectin5</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">sectin6</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">sectin7</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">sectin8</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">sectin9</h2>
            <figure class="parallax_item__imgWrap">
                <div class="parallax__item__img"></div>
            </figure>
            <p class="parallax__item__desc">꿈이 없는 사람은 아무 생명력이 없는 인형과 같다.</p>
        </section><!-- //section9 -->
    </div>
</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>
            <!-- li*9{#section$ offset() : }>span.offset${px} -->
        </ul>
    </div>
</aside>
<footer id="footer">
    <a href="mailto:jo0132@naver.com">jo0132@naver.com</a>
</footer>
  • 이 HTML 문서에는 헤더, 내비게이션 바, 그리고 여러 개의 다양한 내용을 가진 섹션들이 포함되어 있습니다.
  • 헤더에는 웹사이트의 제목, 설명, 그리고 다양한 페이지로의 링크를 포함한 내비게이션 메뉴가 있습니다. 현재 활성화된 페이지는 "active" 클래스가 해당 목록 항목에 추가되어 표시됩니다.
  • 내비게이션 바는 "nav" 요소 내에 포함되어 있으며 페이지의 다양한 섹션으로의 링크 목록을 가지고 있습니다. 각 섹션은 ID (예: "#section1", "#section2" 등)로 식별됩니다.
  • 페이지의 본문 내용은 "main" 요소에 포함되어 있으며 "main" 요소에는 ID가 "main"인 여러 개의 섹션이 있습니다. 각 섹션에는 제목, 이미지, 그리고 설명이 포함되어 있습니다.

CSS

#header {
    position: absolute;
    left: 20px;
    top: 20px;
}
#header h1 {
    margin-bottom: 0.3em;
}
#header ul {
    margin-top: 0.6em;
}
#header li {
    display: inline-block;
}

#header li a {
    color: #fff;
    border: 1px solid #fff;
    width: 30px;
    height: 30px;
    line-height: 30px;
    display: inline-block;
    border-radius: 50%;
    text-align: center;
}
#header li.active a {
    background: #fff;
    color: #000;
}

#footer {
    text-align: center;
    padding: 100px 0;
}
#footer a {
    color: #fff;
    font-size: 14px;
}
#footer a:hover {
    text-decoration: underline;
}

/* parallax__nav */
.parallax__nav {
    position: fixed;
    right: 20px;
    top: 20px;
    z-index: 2000;
    background-color: rgba(0,0,0,0.4);
    padding: 20px 30px;
    border-radius: 50px;
}
.parallax__nav li{
    display: inline;
    margin: 0 5px;
}
.parallax__nav li a {
    display: inline-block;
    padding: 5px 20px;
    text-align: center;
    line-height: 30px;
    color: #fff;
}
.parallax__nav li.active a {
    background-color: #fff;
    color:#000;
    border-radius: 20px;
    box-sizing: content-box;
}
/* parallax__wrap */
.parallax__wrap {
    max-width: 1600px;
    width: 98%;
    margin: 0 auto;
    /* background-color: rgba(255,255,255,0.1); */

}
.parallax__item {
    width: 1000px;
    max-width: 70vw;
    margin: 30vw auto;
    /* background-color: rgba(255,255,255,0.3); */
    margin-right: 0;
    position: relative;
    padding-top: 8vw;
}
.parallax__item:nth-child(even) {
    margin-left: 0;
    text-align: right;
}
.parallax__item__num {
    font-size: 35vw;
    font-weight: 100;
    font-family: Lato;
    position: absolute;
    left: -5vw;
    top: -16vw;
    opacity: 0.07;
    z-index: -2;
}
.parallax__item:nth-child(even) .parallax__item__num {
    left: auto;
    right: -5vw;
}
.parallax__item__title {
    font-weight: bold;
}
.parallax_item__imgWrap {
    width: 100%;
    padding-bottom: 56.25%;
    position: relative;
    z-index: -1;
}
.parallax__item__img {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-image: url(../img/Effect_bg01-min.jpg);
    background-size: cover;
    filter: saturate(0%);
    transition: all 1s;
}
.parallax__item:nth-child(1) .parallax__item__img {
    background-image: url(../img/Effect_bg01-min.jpg);
}
.parallax__item:nth-child(2) .parallax__item__img {
    background-image: url(../img/Effect_bg02-min.jpg);
}
.parallax__item:nth-child(3) .parallax__item__img {
    background-image: url(../img/Effect_bg03-min.jpg);
}
.parallax__item:nth-child(4) .parallax__item__img {
    background-image: url(../img/Effect_bg04-min.jpg);
}
.parallax__item:nth-child(5) .parallax__item__img {
    background-image: url(../img/Effect_bg05-min.jpg);
}
.parallax__item:nth-child(6) .parallax__item__img {
    background-image: url(../img/Effect_bg06-min.jpg);
}
.parallax__item:nth-child(7) .parallax__item__img {
    background-image: url(../img/Effect_bg07-min.jpg);
}
.parallax__item:nth-child(8) .parallax__item__img {
    background-image: url(../img/Effect_bg08-min.jpg);
}
.parallax__item:nth-child(9) .parallax__item__img {
    background-image: url(../img/Effect_bg09-min.jpg);
}
.parallax__item:nth-child(10) .parallax__item__img {
    background-image: url(../img/Effect_bg10-min.jpg);
}
.parallax__item__desc {
    font-size: 4vw;
    line-height: 1.4;
    margin-top: -5vw;
    margin-left: -4vw;
    word-break: keep-all;
}
.parallax__item:nth-child(even) .parallax__item__desc {
    margin-left: auto;
    margin-right: -4VW;
}
.parallax__info {
    position: fixed;
    left: 20px;
    bottom: 20px;
    background-color: rgba(0, 0, 0, 0.6);
    color: #fff;
    padding: 20px;
    border-radius: 10px;
    line-height: 1.4;
}

@media (max-width: 1200px) {
    .parallax__nav {
        padding: 10px;
        background-color: rgba(0,0,0,0.9);
        right: 0;
        left: 10px;
        top: 10px;
        border-radius: 5px;
        text-align: center;
    }
    .parallax__nav li {
        margin: 2px;
    }
    .parallax__nav li a {
        font-size: 12px;
        padding: 0px 14px;
    }
}
  • #header: id가 "header"인 요소에 대한 스타일을 정의합니다. "position: absolute;"는 해당 요소의 위치를 절대적으로 지정하고, "left: 20px;"와 "top: 20px;"은 해당 요소의 왼쪽과 위쪽 여백을 20px로 지정합니다.
  • #header h1: #header 내의 h1 요소에 대한 스타일을 정의합니다. "margin-bottom: 0.3em;"은 해당 요소의 아래쪽 여백을 0.3em으로 지정합니다.
  • #header ul: #header 내의 ul 요소에 대한 스타일을 정의합니다. "margin-top: 0.6em;"은 해당 요소의 위쪽 여백을 0.6em으로 지정합니다.
  • #header li: #header 내의 li 요소에 대한 스타일을 정의합니다. "display: inline-block;"은 해당 요소를 가로로 나열하고, "margin: 0 5px;"은 해당 요소의 왼쪽과 오른쪽 여백을 5px로 지정합니다.
  • #header li a: #header 내의 li 요소 안의 a 요소에 대한 스타일을 정의합니다. "color: #fff;"는 해당 요소의 글자 색상을 흰색으로 지정하고, "border: 1px solid #fff;"는 해당 요소의 테두리를 흰색 1px 실선으로 지정합니다. "width: 30px;"와 "height: 30px;"는 해당 요소의 가로와 세로 크기를 30px로 지정하고, "line-height: 30px;"는 해당 요소의 행 높이를 30px로 지정합니다. "border-radius: 50%;"는 해당 요소의 테두리를 둥글게 처리합니다. "text-align: center;"는 해당 요소의 텍스트를 가운데 정렬합니다.
  • #header li.active a: #header 내의 활성화된 li 요소 안의 a 요소에 대한 스타일을 정의합니다. "background: #fff;"는 해당 요소의 배경색을 흰색으로 지정하고, "color: #000;"는 해당 요소의 글자 색상을 검은색으로 지정합니다.

JAVASCRIPT

window.addEventListener("scroll", () => {
    let scrollTop = window.pageXOffset || window.scrollY || document.documentElement.scrollTop;

    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(".parallax__info .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", () => { ... }); 코드는 scroll 이벤트를 감지하고, 스크롤 이벤트 발생 시 실행할 콜백 함수를 등록합니다.
  • let scrollTop = window.pageXOffset || window.scrollY || document.documentElement.scrollTop; 코드는 현재 스크롤 위치를 확인합니다. window.pageXOffset, window.scrollY, document.documentElement.scrollTop 중에서 값이 있는 첫 번째 값을 scrollTop 변수에 저장합니다.
  • document.querySelectorAll(".parallax__item").forEach((item,index) => { ... }); 코드는 .parallax__item 클래스를 가진 모든 요소에 대해 반복문을 실행합니다. 각 요소의 offsetTop 값을 비교하여 현재 스크롤 위치에 해당하는 내비게이션 메뉴를 활성화합니다.
  • document.querySelectorAll(".parallax__nav li a").forEach(li => { ... }); 코드는 내비게이션 메뉴의 각 링크 요소에 대해 반복문을 실행합니다. 각 링크 요소에 클릭 이벤트를 등록하고, 클릭 시 해당 섹션으로 스크롤 이동을 구현합니다.
  • document.querySelector(".parallax__info .scroll span").innerText = parseInt(scrollTop); 코드는 스크롤 위치 정보를 .parallax__info 클래스 내부의 .scroll 클래스 내부에 있는 span 요소에 업데이트합니다.
  • document.querySelector(".info .offset1").innerText = document.getElementById("section1").offsetTop; 코드는 각 섹션의 offsetTop 정보를 .info 클래스 내부의 각 섹션에 해당하는 .offsetN 클래스 내부의 요소에 텍스트로 업데이트합니다.

 

for문

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

document.querySelector(".info .offset1").innerText = document.getElementById("section1").offsetTop; 을 for문으로 바꿀 수 있는데 .info .offsetX라는 클래스 이름을 가진 요소를 선택하고, 해당 요소의 innerText를 "sectionX"라는 id를 가진 요소의 offsetTop 속성값으로 설정하는 작업을 수행합니다. 이 코드는 HTML 문서에 .info 클래스와 .offset1부터 .offset9까지의 클래스 이름을 가진 요소, 그리고 "section1"부터 "section9"까지의 id를 가진 요소가 있다고 가정하고 실행되어야 합니다.

 

forEach()

forEach
document.querySelectorAll(".info span").forEach((span, index) => {
    span.innerText = document.getElementById("section" + (index + 1)).offsetTop;
});

요소인 span에 띄우게 됩니다. span.innerText) (== span.innerText = document.getElementById("section" + (index + 1)).offsetTop;)
이 내용을 반복하는데  for문, forEach, for in, for of을 활용하는데 
이 때 .info span을 반복해서 요소를 span으로 받고 index을 매개로 받아 사용합니다.

 

for in

const sectionIndex02 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i in sectionIndex02) {
document.querySelector(".info .offset" + sectionIndex02[i]).innerText = document.getElementById("section" + sectionIndex02[i]).offsetTop;
}

먼저 const sectionIndex02 = [1, 2, 3, 4, 5, 6, 7, 8, 9];라는 배열을 만들어고 시작합니다.
배열의 값을 통해 sectionIndex02[i]로 값을 받아 선택자에 원하는 수를 받아올수 있도록합니다. 그렇게 반복문을 작성하면 됩니다.

 

for of

const sectionIndex = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i of sectionIndex) {
document.querySelector(".info .offset" + i).innerText = document.getElementById("section" + i).offsetTop;
}

먼저 const sectionIndex = [1, 2, 3, 4, 5, 6, 7, 8, 9];라는 배열을 만들어고 시작합니다.
배열의 인덱스 값을 통해 i를 받아 선택자에 값을 받아올수 있도록합니다. 그렇게 반복문을 작성하면 됩니다.