오늘은 부드럽게 스크롤되는 패럴랙스 효과를 알아보겠습니당.
자바스크립트 패럴랙스 효과 1
구조
<body class="img01 bg01 font05">
<header id="header">
<h1>Javascript Parallax Effect01</h1>
<p>패럴랙스 이펙트 : 메뉴 효과</p>
<ul>
<li class="active"><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><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>
<!-- //parallax__nav -->
<main id="main">
<div class="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 offsetTop() : <span class="offset1">0</span>px</li>
<li>#section2 offsetTop() : <span class="offset2">0</span>px</li>
<li>#section3 offsetTop() : <span class="offset3">0</span>px</li>
<li>#section4 offsetTop() : <span class="offset4">0</span>px</li>
<li>#section5 offsetTop() : <span class="offset5">0</span>px</li>
<li>#section6 offsetTop() : <span class="offset6">0</span>px</li>
<li>#section7 offsetTop() : <span class="offset7">0</span>px</li>
<li>#section8 offsetTop() : <span class="offset8">0</span>px</li>
<li>#section9 offsetTop() : <span class="offset9">0</span>px</li>
</ul>
</div>
</aside>
<!-- //parallax info -->
<footer id="footer">
<a href="mailto:dsy1943@naver.com">dsy1943@naver.com</a>
</footer>
<!-- //footer -->
</body>
헤더부분은 마우스 이펙트의 구조를 참고하였습니다.
그리고 각 섹션별로 넘어갈 수 있는 네비와, 메인에는 섹션별로 들어갈 번호, 명언, 사진들을 넣어줍니다.
스크립트 짜기
const parallaxInfo = document.querySelector(".parallax__info");
const scroll = parallaxInfo.querySelector(".scroll span");
const parallaxItem = document.querySelectorAll(".parallax__item");
window.addEventListener("scroll", () => {
let scrollTop = window.pageYOffset || 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"
})
});
});
scroll.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;
});
.parallax__info 클래스를 가진 요소와 그 자식 요소들을 변수 parallaxInfo와 scroll에 각각 할당합니다.
.parallax__item 클래스를 가진 모든 요소를 변수 parallaxItem에 NodeList 형태로 할당합니다.
윈도우 객체에 scroll 이벤트 리스너를 추가합니다. 스크롤 이벤트가 발생할 때마다 실행되는 콜백 함수 내에서 다음 작업들을 수행합니다.
현재 스크롤바의 위치를 변수 scrollTop에 할당합니다.
.parallax__item 클래스를 가진 모든 요소에 대해 반복문을 실행합니다.
현재 스크롤바의 위치가 item.offsetTop - 2보다 크거나 같으면, .parallax__nav 클래스를 가진 모든 li 요소에서 active 클래스를 제거하고, 현재 요소의 index 값에 해당하는 li 요소에 active 클래스를 추가합니다.
.parallax__nav 클래스를 가진 모든 li 요소의 a 요소에 클릭 이벤트 리스너를 추가합니다. 클릭 이벤트가 발생할 때마다, 클릭한 a 요소의 href 속성값과 일치하는 요소를 스무스하게 스크롤링합니다.
현재 스크롤바의 위치를 scroll 요소의 내부 텍스트로 출력합니다.
section1부터 section9까지의 offsetTop 값을 각각 info .offset1부터 info .offset9 요소의 내부 텍스트로 출력합니다.
for문
// for문
for(let i=1; i<=9; i++){
document.querySelector(`.info .offset${i}`).innerText = document.getElementById(`section${i}`).offsetTop;
}
i 변수가 1부터 9까지 증가하면서, document.querySelector 메서드를 사용하여 .info .offset${i} 선택자에 해당하는 요소를 가져옵니다. 그리고 document.getElementById 메서드를 사용하여 section${i} ID에 해당하는 요소의 상단 좌표값(offsetTop)을 가져와서, 해당하는 .info .offset${i} 요소의 내부 텍스트로 설정합니다.
forEach()
// forEach()
offSet.forEach((el, i) => {
offSet[i].innerHTML = document.getElementById(`section${i + 1}`).offsetTop;
});
el: 현재 요소
i: 현재 요소의 인덱스
그리고 각 요소의 innerHTML을 해당하는 section의 offsetTop 값으로 설정합니다.
따라서, offSet 배열의 각 요소는 HTML 요소를 참조하고 있으며, id가 "section" + (index + 1)인 요소의 offsetTop 값으로 설정됩니다.
for in
// for in
for (let i in offSet) {
offSet[i].innerHTML = document.getElementById(`section${parseInt(i) + 1}`).offsetTop;
}
for...in문은 offSet 객체의 각 프로퍼티의 이름을 i 변수에 할당하여 루프를 수행합니다. parseInt(i) + 1은 프로퍼티의 이름에서 숫자를 추출하여 1을 더해주는 과정으로, getElementById() 메서드에 전달할 섹션 요소의 ID를 동적으로 생성하기 위한 것입니다.
for of
// for of
let i = 1;
for(let e of offSet){
e.innerHTML = document.getElementById(`section${i}`).offsetTop;
i++;
}
반복문에서 e는 offSet 배열의 각 요소를 나타냅니다.
그리고 각 반복마다 document.getElementById(section${i}).offsetTop을 이용하여 해당 섹션 요소의 offsetTop 속성 값을 가져와 e.innerHTML에 할당합니다. 마지막으로 i를 증가시켜 다음 섹션의 id 값을 찾아 갑니다.
완성된 페이지와 코드보기