parallax effect란?
시차(관측 위치에 따른 물체의 위치나 방향의 차이)를 이용한 효과라는 뜻으로, parallax는 시차라는 뜻이며 천문학에서 사용하는 용어입니다.
멀리 있는 물체는 천천히 움직이고, 가까이 있는 물체는 빨리 움직이는 현상을 의미합니다.
이 현상을 이용하면 입체감, 실체감을 높여서 보다 인상적인 디자인을 할 수 있습니다.
시차에 따라 모션을 다르게 줌으로써 움직이는 모션효과를 주는 것이므로 위와 같이 스크롤의 top값과 각 섹션의 값을 나타내는 효과를 만들어 보았습니다.
HTML
<!DOCTYPE html>
<html lang="en">
<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="img01 bg01 font01">
<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>
<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 offest() : <span class="offset1">0</span>px</li>
<li>#section2 offest() : <span class="offset2">0</span>px</li>
<li>#section3 offest() : <span class="offset3">0</span>px</li>
<li>#section4 offest() : <span class="offset4">0</span>px</li>
<li>#section5 offest() : <span class="offset5">0</span>px</li>
<li>#section6 offest() : <span class="offset6">0</span>px</li>
<li>#section7 offest() : <span class="offset7">0</span>px</li>
<li>#section8 offest() : <span class="offset8">0</span>px</li>
<li>#section9 offest() : <span class="offset9">0</span>px</li>
</ul>
</div>
</aside>
<!-- //parallax__info -->
<footer id="footer">
<a href="mailto:1346zany@gmail.com">1346zany@gmail.com</a>
</footer>
</body>
</html>
네비게이션을 만들고 Parallax 효과를 사용하여 페이지에 각각 section이라는 섹션을 만들어 해당 섹션의 제목, 이미지, 그리고 문장을 포함해줍니다.
CSS
reset.css
parallax.css
/* header */
#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 */
#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: -15vw;
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%;
background-color: #000;
position: relative;
z-index: -1;
}
.parallax__item__img {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-image: url(../img/effect01-min.jpg);
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
filter: saturate(0%);
transition: all 1s;
}
.parallax__item:nth-child(1) .parallax__item__img{
background-image: url(../img/effect01-min.jpg);
}
.parallax__item:nth-child(2) .parallax__item__img{
background-image: url(../img/effect02-min.jpg);
}
.parallax__item:nth-child(3) .parallax__item__img{
background-image: url(../img/effect03-min.jpg);
}
.parallax__item:nth-child(4) .parallax__item__img{
background-image: url(../img/effect04-min.jpg);
}
.parallax__item:nth-child(5) .parallax__item__img{
background-image: url(../img/effect05-min.jpg);
}
.parallax__item:nth-child(6) .parallax__item__img{
background-image: url(../img/effect06-min.jpg);
}
.parallax__item:nth-child(7) .parallax__item__img{
background-image: url(../img/effect07-min.jpg);
}
.parallax__item:nth-child(8) .parallax__item__img{
background-image: url(../img/effect08-min.jpg);
}
.parallax__item:nth-child(9) .parallax__item__img{
background-image: url(../img/effect09-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;
font-size: 14px;
line-height: 1.4;
}
@media (max-width: 1200px) {
.parallax__nav {
padding: 10px;
background-color: rgba(0, 0, 0, 0.9);
right: 10px;
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;
}
}
script
<script>
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"
})
})
})
//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;
</script>
이벤트 리스너를 사용하여 스크롤 이벤트가 발생하면 현재 스크롤 위치를 변수 scrollTop에 저장합니다.
이후, 모든 ".parallax__item" 요소를 forEach() 메서드를 사용하여 반복합니다.
각 요소의 offsetTop 값을 확인하고, 현재 스크롤 위치에 따라 활성화될 섹션과 네비게이션 메뉴를 결정합니다.
네비게이션 메뉴를 클릭하면 해당 섹션으로 스크롤이 이동하도록 하기 위해, ".parallax__nav li a" 요소를 forEach() 메서드를 사용하여 반복하고합니다.
클릭 이벤트가 발생하면 preventDefault() 메서드를 사용하여 기본 동작을 막고, 클릭한 링크의 href 값을 가져와 해당 섹션으로 smooth하게 스크롤 이동하도록 scrollIntoView() 메서드를 실행합니다.
현재 스크롤 위치와 각 섹션의 offsetTop 값을 화면에 출력하기 위해, 해당 요소를 찾아 innerText 값을 변경합니다.
info의 각 section 다른 방법으로 좌표값 구하기
for
//for
for(i=1; i<=9; i++){
document.querySelector(".info .offset"+i).innerText = document.getElementById("section" +i).offsetTop;
}
for문으로 9번 반복시키고 i 값을 offset뒤에 붙여주면 값이 구해집니다.
forEach
document.querySelectorAll(".info li").forEach((e, i) => {
document.querySelector(".info .offset" + (i + 1)).innerText = document.getElementById("section" + (i + 1)).offsetTop;
});
querySelectorAll로 info의 li를 모두 선택해주고 forEach문을 통해 구해줍니다.
i값은 0부터 시작하므로 +1을 해주면 좌표값이 구해집니다.
for in
const infoList = document.querySelectorAll(".info li");
for (let i in infoList) {
document.querySelector(".info .offset" + (parseInt(i) + 1)).innerText = document.getElementById("section" + (parseInt(i) + 1)).offsetTop;
}
infoList안에 info의 li를 선택해서 넣어주고
for in문을 이용해 문자열을 숫자로 반환해주는 paraseInt를 사용해서 구해줍니다.
for of
for (const [i, el] of document.querySelectorAll(".info ul li span").entries()){
el.innerHTML = document.getElementById("section"+(parseInt(i)+1)).offsetTop;
}
document.querySelectorAll(".info ul li span")으로 선택한 모든 span 요소들에 대해서 반복문을 실행하고, 각각의 span 요소의 내용을 해당하는 섹션 요소의 offsetTop 값으로 변경해줍니다.
offsetTop 은 해당 요소가 위치한 부모 요소 상단으로부터의 거리를 나타내며, getElementById("section"+(parseInt(i)+1))은 "section" + (해당 요소의 인덱스 + 1)와 같은 id를 가진 요소를 찾아 반환합니다.