GSAP 패럴랙스 이펙트 : 나타나기 효과
소개
안녕하세요! 웹스토리보이입니다. 이번에는 스크롤을 내리면 하나씩 나타나는 효과를 만들어 보겠습니다. 나타나기 효과는 이미지에 줄 수도 있고, 텍스트에 줄 수도 있습니다. 해당 요소에 이름에 각각 스크립트를 설정하면 조금 번거롭기 때문에 클래스 이름으로 설정할 수도 있습니다. 내가 원하는 효과의 이름을 작성하면, 그거에 맞추어 스크립트를 작성하면 됩니다. 기존과는 약간 다를 수 있지만 기본 원리는 동일하기 때문에 어렵지는 않습니다. 그럼 시작해 볼까요? 😇
1. 기본 구조 만들기
1-1. 준비하기
GSAP를 배우는 시간이기 때문에 HTML/CSS 코딩은 생략하겠습니다. 기본 코딩은 복사해서 사용하겠습니다. 우선 웹폰트를 설정하고 GSAP에서 필요한 파일을 미리 셋팅해 놓겠습니다. GSAP는 자주 업데이트가 되기 때문에 제일 최선 버전을 사용하는게 좋습니다.
<!DOCTYPE html>
<html lang="ko">
<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>GSAP Scroll Effect</title>
<!-- 웹폰트 설정 -->
<link href="https://webfontworld.github.io/NexonLv1Gothic/NexonLv1Gothic.css" rel="stylesheet">
</head>
<body>
<!-- GSAP 라이브러리 설정 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.5/ScrollTrigger.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.1/ScrollToPlugin.min.js"></script>
</body>
</html>
1-2. 기본 셋팅하기
소스는 그대로 복사해서 사용하겠습니다.
<!DOCTYPE html>
<html lang="ko">
<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>GSAP Scroll Effect</title>
<link href="https://webfontworld.github.io/NexonLv1Gothic/NexonLv1Gothic.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@100&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
}
a {
color: #fff;
text-decoration: none;
}
body {
color: #fff;
font-family: "NexonLv1Gothic";
font-weight: 300;
background-color: #111;
}
#parallax__title {
position: fixed;
left: 20px;
top: 20px;
z-index: 1000;
}
#parallax__title h1 {
font-size: 30px;
border-bottom: 1px dashed #fff;
margin-bottom: 10px;
padding-bottom: 5px;
font-weight: 400;
display: inline-block;
}
#parallax__title p {
font-size: 16px;
}
#parallax__title ul {
margin-top: 10px;
}
#parallax__title li {
display: inline;
}
#parallax__title li a {
width: 20px;
height: 20px;
border-radius: 50%;
border: 1px dashed #fff;
display: inline-block;
text-align: center;
line-height: 20px;
font-size: 12px;
}
#parallax__title li.active a {
background: #fff;
color: #000;
}
/* parallax__cont */
#parallax__cont {
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); */
text-align: left;
margin-right: 0;
position: relative;
padding-top: 15vw;
}
.parallax__item:nth-child(even) {
margin-left: 0;
text-align: right;
}
.parallax__item__num {
font-size: 35vw;
position: absolute;
left: -5vw;
top: -13vw;
opacity: 0.07;
font-family: "Lato";
font-weight: 100;
}
.parallax__item:nth-child(even) .parallax__item__num {
left: auto;
right: -5vw;
}
.parallax__item__title {
padding-bottom: 5px;
font-weight: 400;
}
.parallax__item__imgWrap {
width: 100%;
padding-bottom: 56.25%;
background: #000;
position: relative;
overflow: hidden;
}
.parallax__item__img {
position: absolute;
left: -5%;
top: -5%;
width: 110%;
height: 110%;
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
filter: saturate(0%);
transition: all 1s;
}
.parallax__item__img:hover {
filter: saturate(100%);
transform: scale(1.025);
}
#section1 .parallax__item__img {
background-image: url(assets/img/images01@2.jpg);
}
#section2 .parallax__item__img {
background-image: url(assets/img/images02@2.jpg);
}
#section3 .parallax__item__img {
background-image: url(assets/img/images03@2.jpg);
}
#section4 .parallax__item__img {
background-image: url(assets/img/images04@2.jpg);
}
#section5 .parallax__item__img {
background-image: url(assets/img/images05@2.jpg);
}
#section6 .parallax__item__img {
background-image: url(assets/img/images06@2.jpg);
}
#section7 .parallax__item__img {
background-image: url(assets/img/images07@2.jpg);
}
#section8 .parallax__item__img {
background-image: url(assets/img/images08@2.jpg);
}
#section9 .parallax__item__img {
background-image: url(assets/img/images09@2.jpg);
}
.parallax__item__desc {
font-size: 4vw;
line-height: 1.4;
margin-top: -5vw;
margin-left: -4vw;
z-index: 100;
position: relative;
word-break: keep-all;
}
.parallax__item:nth-child(even) .parallax__item__desc {
margin-left: auto;
margin-right: -4vw;
}
</style>
</head>
<body>
<header id="parallax__title">
<h1>GSAP Parallax Effect05</h1>
<p>GSAP scrollTrigger - 나타나기 효과</p>
<ul>
<li><a href="gsap01.html">1</a></li>
<li><a href="gsap02.html">2</a></li>
<li><a href="gsap03.html">3</a></li>
<li><a href="gsap04.html">4</a></li>
<li class="active"><a href="gsap05.html">5</a></li>
<li><a href="gsap06.html">6</a></li>
<li><a href="gsap07.html">7</a></li>
<li><a href="gsap08.html">8</a></li>
<li><a href="gsap09.html">9</a></li>
<li><a href="gsap10.html">10</a></li>
<li><a href="gsap11.html">11</a></li>
<li><a href="gsap12.html">12</a></li>
<li><a href="gsap13.html">13</a></li>
<li><a href="gsap14.html">14</a></li>
<li><a href="gsap15.html">15</a></li>
</ul>
</header>
<!-- //parallax__title -->
<main id="parallax__cont">
<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 -->
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.5/ScrollTrigger.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.1/ScrollToPlugin.min.js"></script>
<script>
</script>
</body>
</html>
2. 스크립트 작업하기
2-1. 나타나기 표현하기
이번에는 ScrollTrigger.create
를 사용하겠습니다. 다중 선택이기 때문에 gsap.utils.toArray
와 forEach
를 사용하겠습니다. 시작점과 끝나는 점을 설정하고 애니메이션을 추가하겠습니다. 애니메이션을 추가하기 위해 콜백함수를 사용할 것입니다. onEnter
콜백함수를 이용하여 animate
함수를 만들겠습니다. GSAP의 fromTo
메서드를 이용해서 요소의 애니메이션 전과 후를 설정하겠습니다. GSAP에서 투명도는 autoAlpha
를 사용합니다. 해당 부분에 오면 투명도가 작동하기 때문에 미리 투명도를 0으로 만들어야 합니다. 그래서 item.style.opacity = "0";
를 설정했습니다. 이렇게 하면 자연스럽게 나타나기가 연출됩니다. 여기에 overwrite: "auto"
속성을 추가하여, 애니메이션이 한번만 작동하도록 할 것입니다. 어떤 요소에 오버 효과 애니메이션을 넣었다면 오버 했을 때 오버 효과는 짧게 한번 또는 여러번이 발생할 수 있습니다. 그럼 오버 효과 명령어는 데이터에 저장이 되는데 여러개가 저장되어 애니메이션이 계속 반복적으로 일어날 수 있습니다. 그 부분을 컨트롤 해주는 것이 overwrite
입니다. 간단하게 생각하면 애니메이션을 한번만 작동할 수 있도록 설정하는 것입니다.
gsap.utils.toArray(".parallax__item__desc").forEach((item) => {
ScrollTrigger.create({
trigger: item,
start: "top 80%",
end: "bottom 20%",
markers: true,
onEnter: () => {animate(item)},
});
item.style.opacity = "0";
});
const animate = (item) => {
gsap.fromTo(item,
{autoAlpha: 0, x: 100, y: 0},
{autoAlpha: 1, x: 0, y: 0, duration: 1.25, overwrite: "auto", ease: "expo"}
);
}
2-2. 클래스 이름으로 설정하기
각 섹션의 텍스트 영역에 클래스 reveal
을 설정했습니다.
<p class="parallax__item__desc reveal">높은 목표를 세우고, 스스로 채찍질 한다.</p>
<p class="parallax__item__desc reveal">결과도 중요하지만, 과정을 더 중요하게 생각한다.</p>
<p class="parallax__item__desc reveal">매 순간에 최선을 다하고, 끊임없이 변화한다.</p>
<p class="parallax__item__desc reveal">모든 일에는 기본을 중요하게 생각한다.</p>
<p class="parallax__item__desc reveal">열정을 잃지 않고 실패에서 실패로 걸어가는 것이 성공이다.</p>
<p class="parallax__item__desc reveal">천 마디 말보단 하나의 행동이 더 값지다.</p>
<p class="parallax__item__desc reveal">조그만 성공에 만족하지 않으며, 방심을 경계한다.</p>
<p class="parallax__item__desc reveal">나는 내가 더 노력할수록 운이 더 좋아진다는 걸 발견했다.</p>
<p class="parallax__item__desc reveal">나는 내가 더 노력할수록 운이 더 좋아진다는 걸 발견했다.</p>
선택 요소도 reveal
로 변경했습니다. 이렇게 설정해도 애니메이션은 변함이 없이 똑같이 나옵니다.
gsap.utils.toArray(".reveal").forEach((item) => {
ScrollTrigger.create({
trigger: item,
start: "top 80%",
end: "bottom 20%",
markers: true,
onEnter: () => {animate(item)},
});
item.style.opacity = "0";
});
const animate = (item) => {
gsap.fromTo(item,
{autoAlpha: 0, x: 100, y: 0},
{autoAlpha: 1, x: 0, y: 0, duration: 1.25, overwrite: "auto", ease: "expo"}
);
}
2-3. 애니메이션 위치 설정하기
애니메이션이 오른쪽에서 왼쪽으로만 나오면 이상하니, 짝수 섹션은 왼쪽에 오른쪽으로 나오게 설정할 것입니다. reveal
클래스 옆에 reveal_LTR
을 설정합니다.
<p class="parallax__item__desc reveal">높은 목표를 세우고, 스스로 채찍질 한다.</p>
<p class="parallax__item__desc reveal reveal_LTR">결과도 중요하지만, 과정을 더 중요하게 생각한다.</p>
<p class="parallax__item__desc reveal">매 순간에 최선을 다하고, 끊임없이 변화한다.</p>
<p class="parallax__item__desc reveal reveal_LTR">모든 일에는 기본을 중요하게 생각한다.</p>
<p class="parallax__item__desc reveal">열정을 잃지 않고 실패에서 실패로 걸어가는 것이 성공이다.</p>
<p class="parallax__item__desc reveal reveal_LTR">천 마디 말보단 하나의 행동이 더 값지다.</p>
<p class="parallax__item__desc reveal">조그만 성공에 만족하지 않으며, 방심을 경계한다.</p>
<p class="parallax__item__desc reveal reveal_LTR">나는 내가 더 노력할수록 운이 더 좋아진다는 걸 발견했다.</p>
<p class="parallax__item__desc reveal">나는 내가 더 노력할수록 운이 더 좋아진다는 걸 발견했다.</p>
변수 x
와 y
를 설정하고, 클래스에 reveal_LTR
가 있는지 체크 후 위치 값을 설정합니다. 애니메이션 처음의 위치를 변수 값으로 대처하면 상황에 따라 애니메이션을 다르게 설정할 수 있습니다.
const animate = (item) => {
let x = 100;
let y = 0;
if(item.classList.contains("reveal_LTR")){
x = -100;
y = 0;
}
gsap.fromTo(item,
{autoAlpha: 0, x: x, y: y},
{autoAlpha: 1, x: 0, y: 0, duration: 1.25, overwrite: "auto", ease: "expo"}
);
}
2-4. 애니메이션 추가 설정하기
위에서 아래로, 아래에서 위로 애니메이션도 추가 설정하겠습니다. 이렇게 한번 만들어 놓으면, 포폴에서 편하게 사용할 수 있습니다. 이름은 reveal_BTT
와 reveal_TTB
로 설정하겠습니다.
<p class="parallax__item__desc reveal">높은 목표를 세우고, 스스로 채찍질 한다.</p>
<p class="parallax__item__desc reveal reveal_LTR">결과도 중요하지만, 과정을 더 중요하게 생각한다.</p>
<p class="parallax__item__desc reveal reveal_BTT">매 순간에 최선을 다하고, 끊임없이 변화한다.</p>
<p class="parallax__item__desc reveal reveal_LTR">모든 일에는 기본을 중요하게 생각한다.</p>
<p class="parallax__item__desc reveal reveal_TTB">열정을 잃지 않고 실패에서 실패로 걸어가는 것이 성공이다.</p>
<p class="parallax__item__desc reveal reveal_LTR">천 마디 말보단 하나의 행동이 더 값지다.</p>
<p class="parallax__item__desc reveal reveal_BTT">조그만 성공에 만족하지 않으며, 방심을 경계한다.</p>
<p class="parallax__item__desc reveal reveal_LTR">나는 내가 더 노력할수록 운이 더 좋아진다는 걸 발견했다.</p>
<p class="parallax__item__desc reveal reveal_TTB">나는 내가 더 노력할수록 운이 더 좋아진다는 걸 발견했다.</p>
변수 기본 값을 0
으로 설정하고, else if
문을 사용하여, 해당 변수 값을 개별적으로 설정하겠습니다. 아무것도 해당되지 않으면 기본 값이기 때문에 x=100, y=0으로 설정할 것입니다. 이렇게 하면 원하는 요소에 애니메이션을 자유롭게 설정할 수 있습니다.
const animate = (item) => {
let x = 0;
let y = 0;
if(item.classList.contains("reveal_LTR")){
x = -100;
y = 0;
} else if(item.classList.contains("reveal_BTT")){
x = 0;
y = 100;
} else if(item.classList.contains("reveal_TTB")){
x = 0;
y = -100;
} else {
x = 100;
y = 0;
}
gsap.fromTo(item,
{autoAlpha: 0, x: x, y: y},
{autoAlpha: 1, x: 0, y: 0, duration: 1.25, overwrite: "auto", ease: "expo"}
);
}
2-5. 정리하기
투명도 설정을 GSPA 스타일에 맞게 따로 정리를 했습니다. 시작값과 끝나는 값을 주석표시를 하여 기본 값으로 설정하겠습니다. 그럼 섹션이 시작하면 바로 작동하게 됩니다. 이렇게 스크립트를 작성하고 원하는 곳에 reveal
, reveal_LTR
, reveal_BTT
, reveal_TTB
, data-delay="1"
을 작성하면 리빌 효과를 자유롭게 줄 수 있습니다.
const hide = (item) => {
gsap.set(item, {autoAlpha: 0});
}
const animate = (item) => {
let x = 0;
let y = 0;
let delay = item.dataset.delay;
if(item.classList.contains("reveal_LTR")){
x = -100;
y = 0;
} else if(item.classList.contains("reveal_BTT")){
x = 0;
y = 100;
} else if(item.classList.contains("reveal_TTB")){
x = 0;
y = -100;
} else {
x = 100;
y = 0;
}
gsap.fromTo(item,
{autoAlpha: 0, x: x, y: y},
{autoAlpha: 1, x: 0, y: 0, delay: delay, duration: 1.25, overwrite: "auto", ease: "expo"}
);
}
gsap.utils.toArray(".reveal").forEach((item) => {
hide(item);
ScrollTrigger.create({
trigger: item,
// start: "top bottom",
// end: "bottom top",
markers: true,
onEnter: () => {animate(item)},
});
});
3. 마무리
어떤가요? 잘 되시나요? GSAP를 이용하여 나타나기 효과를 쉽게 작성할 수 있습니다. 이렇게 한번만 만들어 놓으면 자유롭게 사용이 가능하고, 내가 원하는 기능이나 추가 옵션을 설정할 수 있습니다. 자기가 직접 스크립트를 짜고, 이해해야 수정이 가능합니다. 최대한 이해하려고 노력하고, 이해가 안되면 외우시면 됩니다. 외우다 보면 이해가 되고, 이해가 되다보면 외워집니다. 결국 반복적으로 연습하시면 됩니다. ^^ 수고하셨습니다. 🥰😍
예제 목록
- 1. GSAP 패럴랙스 이펙트 : 기본 애니메이션
- 2. GSAP 패럴랙스 이펙트 : Pin 애니메이션
- 3. GSAP 패럴랙스 이펙트 : Pin 배경 고정하기
- 4. GSAP 패럴랙스 이펙트 : 이질감 효과
- 5. GSAP 패럴랙스 이펙트 : 나타나기 효과
- 6. GSAP 패럴랙스 이펙트 : 텍스트 효과
- 7. GSAP 패럴랙스 이펙트 : 배경색 효과
- 8. GSAP 패럴랙스 이펙트 : 진행바 효과
- 9. GSAP 패럴랙스 이펙트 : 메뉴 이동 효과
- 10. GSAP 패럴랙스 이펙트 : 메뉴 축소 효과
- 11. GSAP 패럴랙스 이펙트 : 메뉴 숨기기 효과
- 12. GSAP 패럴랙스 이펙트 : 가로 효과
- 13. GSAP 패럴랙스 이펙트 : 가로/세로 효과
- 14. GSAP 패럴랙스 이펙트 : 가로/세로 나타나기 효과
- 15. GSAP 패럴랙스 이펙트 : 스무스 효과
댓글