Blinking Hello Kitty Angel

javascript

패럴렉스 효과 1 (포트폴리오 참고용)

xoouxa 2023. 4. 18. 23:56

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

- Frederick Philips Brooks
Mythical Man-Month 저자
728x90

안녕하세요 ヾ(≧▽≦*)o 오늘은 패럴렉스 효과를 사용해 밑으로 스크롤하면 사진이 스크롤되면서 명언이 나오고, 사진의 좌표값을 구하고 그 좌표값을 화면에 나타내 보도록 하겠습니다!

이 작업은 포트폴리오를 작성할 때 자주 사용되는 양식이라고 하니 포폴을 준비하시는 분들은 제 블로그를 참고하셔서 멋진 포트폴리오를 만들어 보시면 좋을 것 같습니다 ! 🥰

 

💛완성화면 입니다.

 

 

👏 CSS는 너무 길어서 제 github로 오시면 소스를 확인하실 수 있습니다.

 

메뉴를 누르시면 1-9 중 원하시는 곳으로 넘어가게 됩니다.

 

HTML

<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 -->

9개의 사진과 명언을 넣어주기 위해 nav를 만들고 그 안에 ul을 만들고 li를 9개 만들어 1-9번까지의 섹션과 메뉴를 만들어 줍니다.

 

<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">section01</h2>
    <figure class="parallax__item__imgWrap">
        <div class="parallax__item__img"></div>
    </figure>
    <p class="parallax__item__desc">결과도 중요하지만, 과정을 더 중요하게 생각한다.</p>
</section>
<!-- section01 -->

main에는 사진과 명언이 9개 나와야 하므로 하나씩 전체적인 wrap과 nav에서 지정해 준 number, title, img,명언을 넣어줘 섹션01번으로 지정해 줍니다.

 

9개를 만들어줘야해서 section을 9개 더 만들어 줍니다.

<section id="section2" class="parallax__item">
<span class="parallax__item__num">02</span>
<h2 class="parallax__item__title">section02</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">우리 모두는 인생에서 만회할 기회라 할 수 있는 큰 변화를 경험한다</p>
</section>
<!-- section02 -->

<section id="section3" class="parallax__item">
<span class="parallax__item__num">03</span>
<h2 class="parallax__item__title">section03</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">다음에 벌어질지 모를 장벽을 걱정하여 미래를 향한 걸음을 멈춰선 안된다.</p>
</section>
<!-- section03 -->

<section id="section4" class="parallax__item">
<span class="parallax__item__num">04</span>
<h2 class="parallax__item__title">section04</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">나 스스로가 풍요로운 사람이 되려 노력해야 한다</p>
</section>
<!-- section04 -->

<section id="section5" class="parallax__item">
<span class="parallax__item__num">05</span>
<h2 class="parallax__item__title">section05</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">자신에 대한 평판은 신경쓰지마라</p>
</section>
<!-- section05 -->

<section id="section6" class="parallax__item">
<span class="parallax__item__num">06</span>
<h2 class="parallax__item__title">section06</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">이상과 꿈을 버리지 마라</p>
</section>
<!-- section06 -->

<section id="section7" class="parallax__item">
<span class="parallax__item__num">07</span>
<h2 class="parallax__item__title">section07</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">명사형이 아닌 동사형의 삶을 살아라</p>
</section>
<!-- section07 -->

<section id="section8" class="parallax__item">
<span class="parallax__item__num">08</span>
<h2 class="parallax__item__title">section08</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">도달할 수 있는 최고의 삶과 마주하라</p>
</section>
<!-- section08 -->

<section id="section9" class="parallax__item">
<span class="parallax__item__num">09</span>
<h2 class="parallax__item__title">section09</h2>
<figure class="parallax__item__imgWrap">
<div class="parallax__item__img"></div>
</figure>
<p class="parallax__item__desc">우리는 삶이 단 한 번 밖애 존재하지 않는단 사실을 망각하고 산다</p>
</section>
<!-- section09 -->
</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>
<!-- parallax__info -->

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

다음은 사용자에게 사용자가 원하는 사진과 명언을 보여주기 위한 작업을 해줍니다.

이 작업은 사용자가 상단에 있는 메뉴 1-9까지 중 무작위로 메뉴 하나를 누르면 그 누른 메뉴로 이동하도록 작업해 주어야 합니다.

그렇게 해주기 위해선 사용자가 클릭한 메뉴와 일치하는 사진의 값이 어디인 지 즉각적으로 계산하고 그 값에 맞춰진 이미지를 보여주기 위한 설정을 해줘야 합니다.

조금 복잡하긴 하지만 ..! 더 디테일하고 완성도 높은 사이트와 포폴을 위해서 해보도록 하겠습니다..!!!

또한 footer를 꼭 설정해 주어야 메뉴 9를 눌렀을 때 9번째에 있는 사진과 명언이 다른 사진과 명언들처럼 제대로 값이 설정됩니다.

 

SCRIPT

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

메뉴와 section이 일치하도록 만들어주기 위해선 페이지의 Y값을 구해야 합니다.

Y값을 구하기 위해서 사용할 수 있는 메서드는 offset, scroll, document.documentElement.scrollTop 이 있습니다.

console.log를 사용해 확인해 보면 페이지의 Y값을 확인할 수 있습니다.

이제 이 세가지를 이용해 페이지의 좌표값들을 구해보도록 하겠습니다.

 

스크롤 하면 스크롤 순대로 메뉴 버튼에 불이 들어오게 해주기

//스크롤 하면 스크롤 순대로 메뉴 버튼에 불이 들어오게 해주기
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");
}
});

스크롤바를 내리면 section과 상단에 메뉴바를 싱크가 맞게 해주도록 작업해 주어야 합니다.

예를 들어 , 화면이 section1에 있으면 메뉴1에 싱크가 맞춰져야 합니다.

그 작업을 해주기 위해 forEach를 사용해 줍니다.

scrollTop이 offsetTop에 -2를 해준 이유는 각 섹션의 맨 위에 대한 오프셋 값을 구하고 이 값에서 -2를 빼 현재 스크롤 위치가 해당 섹션을 지나갔을 때 조건을 충족시키도록 합니다.

이렇게 설정해주면 메뉴 항목이 지나치게 빨리 활성화 돼 바뀌는 것을 방지할 수 있습니다.

 

다음은 모든 클래스가 parallax__nav인 요소의 자식인 li 요소를 선택하고, forEach 메서드를 사용해 선택된 각 요소에 대해 콜백함수를 실행합니다.

li.classList.remove("active");는 모든 li 요소에서 active 클래스를 제거하는 이유는 스크롤할 때마다 현재 화면에 보이는 요소에 해당하는 내비게이션을 강조하기 위한 코드이지만 스크롤 위치가 변경될 때마다 모든 li 요소에서 active 클래스를 제거해 이전에 강조 됐던 메뉴 항목의 강조 효과를 제거하는 것입니다.

마지막으로 index+1을 해주는 이유는 index 변수는 0부터 시작하는데 .parallax__nav li의 첫 번째 요소는 1번째이기 때문에 index에 1을 더해 실제 보여지는 순서에 맞게 해주기 위해 +1 작업을 해줍니다.

 

상단에 있는 메뉴바를 클릭해서 이동하기

document.querySelectorAll(".parallax__nav li a").forEach(li => {
li.addEventListener("click", (e) => {
    e.preventDefault();         //메뉴를 클릭해도 이미지가 움직이지 않음
    document.querySelector(li.getAttribute("href")).scrollIntoView({
        behavior: "smooth"  //메뉴를 클릭하면 해당하는 이미지로 이동할 수 있음
    })
})
})

이 코드는 페이지 내에 있는 메뉴를 클릭했을 때 해당 메뉴가 가리키는 이미지로 부드럽게 스크롤 하는 코드입니다.

parallax__nav 클래스를 가진 요소들 중 li 요소 아래 있는 모든 a 요소를 찾아 반복문을 수행하고 클릭 이벤트 리스너를 추가합니다.

그리고 a 요소의 href 속성에 저장 된 값을 찾아 그 이미지로 스크롤 되도록 설정합니다.

속성을 smooth로 설정해 스크롤이 부드럽게 이뤄지도록 작업합니다.

href 속성은 ID가 section1인 요소로 이동하는 링크를 나타냅니다.

 

현재 스크롤 위치 출력하기

//위에 값 넣기
document.querySelector(".scroll span").innerHTML = parseInt(scrollTop);

class가 scroll인 요소 중 첫 번째 span 요소를 선택합니다.

그리고innerHTML을 통해 해당 요소 안의 내용을 변경합니다.

이때 parseInt는 현재 스크롤 위치가 소수점까지 나오니 소수점을 모두 버리고 정수형으로 변환한 값을 의미합니다.

 

1-9개의 섹션의 좌표값을 나타내주기 위한 첫 번째 방법

// 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;

첫 번째 방법은 일일히 1-9개의 값을 설정해 주는 것입니다.

이렇게 하면 가독성도 떨어지고 설정해줘야 하는 값이 10000개가 된다면 실질적으로 설정이 어렵기 때문에

for, forEach, for of, for in문을 사용해 작업해 주도록 하겠습니다.

 

for문

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

먼저 1-9개의 섹션이 있기 때문에 10까지 설정해 줍니다.

i가 1일 땐 .offset1 클래스를 가진 요소의 innerTEXT 값을 section1 요소에 offsetTop 값으로 설정합니다.

이렇게 하면 .offset1 클래스를 가진 요소에 section1 요소의 offsetTop 값을 표시할 수 있습니다.

 

offsetTop 속성은 해당 요소의 상위 요소로부터의 거리를 나타내며 innerTEXT 속성은 해당 요소의 내용을 나타냅니다.

따라서 위 코드는 .info 요소 내부에 있는 .offset 클래스를 가진 요소에 각각 section 요소의 위치 정보를 표시하는 역할을 합니다.

 

 

forEach

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

일단 #section$의 offsetTop값을 받아서(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 Of

const arr = document.querySelectorAll(".info ul li");
let index = 0;
for(const el of arr){
el.querySelector("span").innerHTML = document.getElementById(`section${parseInt(index)+1}`).offsetTop;
index++;
}

.info ul li 선택자로 선택된 모든 요소들을 가져옵니다.

각 요소의 자식 요소중 span 요소를 선택해 innerHTML 속성을 설정합니다.

이 때 innerHTML 속성에 할당된 값은 해당 li 요소에 해당하는 섹션의 offsetTop 값입니다.

이 값을 할당하는 방법은 document.getElementById() 함수를 사용해 해당 section 요소를 찾고 그 요소의 offsetTop 속성으을 할당하면 됩니다.

여기서 index  변수는 0으로 초가화 되고 각 li 요소에 span에 할당 될 offsetTop 값의 인덱스를 나타냅니다.

parseInt(index)+1을 통해 1투너 시작 하는 섹션 ID 값을 얻고, 이를 document.getElementById() 함수의 인자로 전달해 해당 섹션 요소를 찾습니다.

 

 

for In

const arr = document.querySelectorAll(".info ul li")
for(let index in arr){
    const el = arr[index];
    el.querySelector("span").innerText = document.getElementById(`section${parseInt(index)+1}`).offsetTop;
}

각 요소의 자식 요소 중 첫 번째 span 요소에 접근하여, 해당 요소의 innerText 속성을 해당 섹션의 오프셋 값으로 설정합니다.

하지만 for...in 루프는 배열에 대한 반복을 수행할 때는 권장되지 않습니다. for...in 루프는 객체의 속성을 열거하는 것이 주목적이기 때문입니다. 따라서 배열 요소에 대한 반복을 수행할 때는 일반적으로 for...of 루프를 사용하는 것이 좋습니다.

 

코드보기 -css로 들어가시면 css들이 나오고 html에 들어가시면 위에 작업한 html이 나옵니다.

https://github.com/leeyouna21/web2023/tree/master/javascript/parallax

 

GitHub - leeyouna21/web2023: 수업시간에 배운 사이트입니다.

수업시간에 배운 사이트입니다. Contribute to leeyouna21/web2023 development by creating an account on GitHub.

github.com

 

 

🥰 오늘도 감사합니다.