본문 바로가기
Tutorial/GSAP

02. GSAP Parallax Effect : Pin 애니메이션

by @webstoryboy 2023. 6. 19.
Tutorial/webd

GSAP 패럴랙스 이펙트

by @webs 2023. 06. 01.
02
GSAP Parallax Effect : Pin 애니메이션
난이도 쉬움

소개

안녕하세요! 웹스토리보이입니다. GSAP Parallax Effect Pin 애니메이션에 대해서 배워보겠습니다. scrollTrigger 속성 중에 pin 애니메이션 기억하시죠? 무엇인가를 고정시킬 때 사용하는 Pin 속성을 이용해서 다양한 애니메이션 작업할 수 있습니다. 그럼 시작해보겠습니다. 렛츠기릿 🥳

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">
    <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 {
            overflow: hidden;
        }
        .parallax__item {
            width: 100%;
            height: 100vh;
            position: relative;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #section2, 
        #section4, 
        #section6, 
        #section8 {
            background-color: #222;
        }
        .parallax__item__num {
            position: absolute;
            right: 20px;
            bottom: 20px;
            font-size: 5vw;
            line-height: 1;
        }
        .parallax__item__img {
            width: 10vw;
            height: 10vw;
            margin: 1vw;
            background-color: #fff;
            background-size: cover;
            background-position: center;
        }
        #section1 .parallax__item__img {
            background-image: url(assets/img/images14.jpg);
        }
        #section2 .parallax__item__img {
            background-image: url(assets/img/images15.jpg);
        }
        #section3 .parallax__item__img {
            background-image: url(assets/img/images03.jpg);
        }
        #section4 .parallax__item__img {
            background-image: url(assets/img/images04.jpg);
        }
        #section5 .parallax__item__img {
            background-image: url(assets/img/images05.jpg);
        }
        #section6 .parallax__item__img {
            background-image: url(assets/img/images06.jpg);
        }
        #section7 .parallax__item__img {
            background-image: url(assets/img/images07.jpg);
        }
        #section8 .parallax__item__img {
            background-image: url(assets/img/images08.jpg);
        }
        #section9 .parallax__item__img {
            background-image: url(assets/img/images09.jpg);
        }

        /* option */
        #section3 .parallax__item__img {
            width: 100px;
            height: 100px;
            border-radius: 50px;
        }
        #section5 {
            flex-direction: column;
        }
        #section5 .parallax__item__text,
        #section6 .parallax__item__text {
            font-size: 5vw;
            text-transform: uppercase;
            font-weight: bold;
        }
        #section7 .parallax__item__text {
            font-size: 5vw;
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            background-color: #111;
            padding: 30px;
            width: 100%;
            text-align: center;
        }
        #section8 {
            flex-direction: column;
        }
        #section8 .parallax__item__text {
            font-size: 2vw;
            line-height: 1.5;
            text-transform: uppercase;
        }
        #section8 .parallax__item__img {
            width: 100px;
            height: 100px;
            border-radius: 50%;
        }
        #section9 .parallax__item__img {
            width: 200px;
            height: 200px;
            border-radius: 50%;
        }
    </style>
</head>
<body>
    <header id="parallax__title">
        <h1>GSAP Parallax Effect02</h1>
        <p>GSAP scrollTrigger - Pin 애니메이션</p>
        <ul>
            <li><a href="gsap01.html">1</a></li>
            <li class="active"><a href="gsap02.html">2</a></li>
            <li><a href="gsap03.html">3</a></li>
            <li><a href="gsap04.html">4</a></li>
            <li><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>
            <div class="parallax__item__img"></div>
        </section>
        <!-- //section1 -->

        <section id="section2" class="parallax__item">
            <span class="parallax__item__num">02</span>
            <div class="parallax__item__img i1"></div>
            <div class="parallax__item__img i2"></div>
            <div class="parallax__item__img i3"></div>
        </section>
        <!-- //section2 -->

        <section id="section3" class="parallax__item">
            <span class="parallax__item__num">03</span>
            <div class="parallax__item__img"></div>
            <div class="parallax__item__img"></div>
            <div class="parallax__item__img"></div>
            <div class="parallax__item__img"></div>
            <div class="parallax__item__img"></div>
            <div class="parallax__item__img"></div>
        </section>
        <!-- //section3 -->

        <section id="section4" class="parallax__item">
            <span class="parallax__item__num">04</span>
            <div class="parallax__item__img"></div>
        </section>
        <!-- //section4 -->

        <section id="section5" class="parallax__item">
            <span class="parallax__item__num">05</span>
            <div class="parallax__item__text t1">section5</div>
            <div class="parallax__item__text t2">section5</div>
            <div class="parallax__item__text t3">section5</div>
            <div class="parallax__item__text t4">section5</div>
        </section>
        <!-- //section5 -->

        <section id="section6" class="parallax__item">
            <span class="parallax__item__num">06</span>
            <div class="parallax__item__text">section6</div>
        </section>
        <!-- //section6 -->

        <section id="section7" class="parallax__item">
            <span class="parallax__item__num">07</span>
            <div class="parallax__item__text t1">코딩이란</div>
            <div class="parallax__item__text t2">프로그래밍 코드를</div>
            <div class="parallax__item__text t3">어딘가에</div>
            <div class="parallax__item__text t4">적는 것을 말한다</div>
            <div class="parallax__item__text t5">코딩은</div>
            <div class="parallax__item__text t6">누구나</div>
            <div class="parallax__item__text t7">할 수 있다.</div>
        </section>
        <!-- //section7 -->

        <section id="section8" class="parallax__item">
            <span class="parallax__item__num">08</span>
            <div class="parallax__item__text t1">section8 title1</div>
            <div class="parallax__item__text t2">section8 title2</div>
            <div class="parallax__item__text t3">section8 title3</div>
            <div class="parallax__item__img i1"></div>
        </section>
        <!-- //section8 -->

        <section id="section9" class="parallax__item">
            <span class="parallax__item__num">09</span>
            <div class="parallax__item__img"></div>
        </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. 기본 애니메이션

pin 속성을 이용해서 고정을 시키고 애니메이션을 주는 기법입니다. 패럴랙스에 많이 사용하는 방법 중에 하나입니다. 이번에 스크롤 트리거는 ScrollTrigger.create를 이용해서 작업하겠습니다. 예제 1번에 사용했던 방법과 동일합니다. 단지 방법의 차이가 있을 뿐입니다. 둘다 많이 사용하기 때문에 둘다 사용하는 방법을 알고 있어야 합니다. anticipatePin 속성은 핀 효과를 조금 더 자연스럽게 연출하기 위한 속성입니다. 아래 소스와 같이 작성하면 이미지는 회전을 하면서 작아지고 커지는 효과를 연출하였습니다.

// 01 : 이미지 애니메이션 주기
const ani1 = gsap.timeline();
ani1.to("#section1 .parallax__item__img", {rotation: 720, scale: 0, borderRadius: 200})
    .to("#section1 .parallax__item__img", {rotation: 0, scale: 1, borderRadius: 20})

ScrollTrigger.create({
    animation: ani1,
    trigger: "#section1",
    start: "top top",
    end: "+=2000",
    scrub: true,
    pin: true,
    anticipatePin: 1,
    markers: false
});

2-2. 이미지 순차적으로 나오기

이번에는 이미지를 순차적으로 나오게 작업하였습니다. 두번째 섹션이 되면 화면은 고정되고 이미지가 하나씩 나옵니다. from 메서드는 to 메서드의 반대라고 생각하면 됩니다. 현재 y의 값은 0이기 때문에 애니메이션을 -100에서 0으로 준다고 생각하면 됩니다. end: "+=2000" 속성은 요소가 끝나는 부분부터 2000px을 더 준다고 생각하면 끝나는 점이 더 멀어지기 때문에 속도 조절이나 길이 조정이 가능합니다.

// 02 : 이미지 순차적으로 나오기
const ani2 = gsap.timeline();
ani2.from("#section2 .i1", {y: -100, autoAlpha:0, borderRadius: 200})
    .from("#section2 .i2", {y: 100, autoAlpha:0, borderRadius: 200})
    .from("#section2 .i3", {y: -100, autoAlpha:0, borderRadius: 200});

ScrollTrigger.create({
    animation: ani2,
    trigger: "#section2",
    start: "top top",
    end: "+=2000",
    scrub: true,
    pin: true,
    anticipatePin: 1,
    markers: false
});

2-3. 이미지 랜덤으로 떨어지기

gsap.timeline() 메서드를 주면 요소에 순차적으로 애니메이션을 줄 수 있습니다. 변수에 저장을 하고 해당 요소에 애니메이션을 주면 편합니다. 이번에는 이미지가 랜덤으로 나오게 설정했습니다. autoAlpha는 투명도를 설정하고 y 값은 transform: translateY를 말합니다. ease 효과는 움직이는 효과를 말하고 stagger는 이미지들을 말합니다. 이미지가 여러개 있기 때문에 순차적으로 random을 설정할 수 있습니다. 이렇게 하면 스크롤 할 때마다 랜덤으로 이미지를 연출할 수 있습니다.

// 03 : 이미지 랜덤으로 떨어지기
const ani3 = gsap.timeline();
ani3.from("#section3 .parallax__item__img", {
    autoAlpha: 0,
    y: -100,
    ease: "back.out(4)",
    stagger: {
        amount: 3,
        from: "random"
    }
})

ScrollTrigger.create({
    animation: ani3,
    trigger: "#section3",
    start: "top top",
    end: "+=3000",
    scrub: true,
    pin: true,
    markers: false,
    anticipatePin: 1
});

2-4. 이미지 축소하기

이미지를 화면에 맞게 width: "100vw", height: "100vh" 설정 후, 지금 크기 값으로 애니메이션 할 수 있도록 from 메서드를 사용하였습니다. 투명도는 0에서 1로 설정하였습니다. 이렇게 하면 전체 이미지가 축소되면서 애니메이션이 연출됩니다. 이 효과도 많이 사용하는 효과입니다.

// 04 : 이미지 축소하기
const ani4 = gsap.timeline();
ani4.from("#section4 .parallax__item__img", {
    autoAlpha:0, 
    scale: 5,
    width: "100vw",
    height: "100vh"
})

ScrollTrigger.create({
    animation: ani4,
    trigger: "#section4",
    start: "top top",
    end: "+=3000",
    scrub: true,
    pin: true,
    markers: false,
    anticipatePin: 1
});

2-5. 텍스트 애니메이션

이번에는 텍스트를 이용해서 작업해보겠습니다. 4개의 텍스트를 만들고 x축으로 300%를 설정했습니다. 퍼센트를 표현할 때에는 xPercent라고 설정합니다. 4개의 요소가 동시에 움직이고 싶다면, 아래와 같이 "text" 문자열을 추가하면 됩니다. 문자열 이름은 랜덤입니다.

// 05 : 텍스트 애니메이션
const ani5 = gsap.timeline();
ani5.to("#section5 .t1", {xPercent: 300}, "text")
    .to("#section5 .t2", {xPercent: -300}, "text")
    .to("#section5 .t3", {xPercent: 300}, "text")
    .to("#section5 .t4", {xPercent: -300}, "text")

ScrollTrigger.create({
    animation: ani5,
    trigger: "#section5",
    start: "top top",
    end: "+=3000",
    scrub: true,
    pin: true,
    markers: true,
    anticipatePin: 1
});

2-6. 텍스트 확대하기

이미지처럼 텍스트 역시 확대하거나 축소가 가능합니다. 텍스트를 이용해서 확대하면서 화면을 전환하는 효과를 많이 사용합니다. scale를 이용하여 60배 정도 확대하고 투명도를 0으로 설정했습니다.

// 06 : 텍스트 확대하기
const ani6 = gsap.timeline();
ani6.to("#section6 .parallax__item__text", {scale: 60, duration: 2, autoAlpha: 1})
    .to("#section6 .parallax__item__text", {autoAlpha: 0})

ScrollTrigger.create({
    animation: ani6,
    trigger: "#section6",
    start: "top top",
    end: "+=4000",
    scrub: true,
    pin: true,
    anticipatePin: 1,
    markers: false
});

2-7. 텍스트 제자리 애니메이션

테스트를 이용하여 제자리애서 변경하는 애니메이션을 구현했습니다. 처음에는 투명도를 0으로 설정하고 밑에서 위로 나오도록 설정했습니다. 텍스트 하나가 끝나고 바로 다음 텍스트가 나오기 때문에 "+=1"을 추가하여 조금 더 자연스럽게 나오도록 했습니다.

// 07 : 텍스트 제자리 애니메이션
const ani7 = gsap.timeline();
ani7.from("#section7 .t1", {autoAlpha: 0, duration: 1, y: 50}, "+=1")
    .from("#section7 .t2", {autoAlpha: 0, duration: 1, y: 50}, "+=1")
    .from("#section7 .t3", {autoAlpha: 0, duration: 1, y: 50}, "+=1")
    .from("#section7 .t4", {autoAlpha: 0, duration: 1, y: 50}, "+=1")
    .from("#section7 .t5", {autoAlpha: 0, duration: 1, y: 50}, "+=1")
    .from("#section7 .t6", {autoAlpha: 0, duration: 1, y: 50}, "+=1")
    .from("#section7 .t7", {autoAlpha: 0, duration: 1, y: 50}, "+=1")

ScrollTrigger.create({
    animation: ani7,
    trigger: "#section7",
    start: "top top",
    end: "+=6000",
    scrub: true,
    pin: true,
    markers: false,
    anticipatePin: 1
});

2-8. 텍스트 애니메이션

이번에는 텍스트를 자연스럽게 나오도록 설정했습니다. 현재 x의 값이 가운데로 설정되어 있기 때문에 from를 이용해서 화면 밖에 왼쪽, 오른쪽에 나올 수 있도록 innerWidth을 설정했습니다. -1을 설정하면 반대로 나오겠죠!

// 08 : 텍스트 애니메이션
const ani8 = gsap.timeline();
ani8.from("#section8 .t1", {x: innerWidth * 1})
    .from("#section8 .t2", {x: innerWidth * -1})
    .from("#section8 .t3", {x: innerWidth * 1})
    .from("#section8 .i1", {x: innerWidth * 1, rotation: 360, scale: 1.5})

ScrollTrigger.create({
    animation: ani8,
    trigger: "#section8",
    start: "top top",
    end: "+=4000",
    scrub: true,
    pin: true,
    markers: false,
    anticipatePin: 1
});

2-9. 텍스트 애니메이션

마지막으로 이미지를 확대해서 없어지는 걸로 마무리했습니다. 이미지가 커진 다음 투명도를 0으로 설정했습니다.

//09 : 이미지 확대하기
const ani9 = gsap.timeline();
ani9.to("#section9 .parallax__item__img", {scale: 13})
    .to("#section9 .parallax__item__img", {autoAlpha: 0})

ScrollTrigger.create({
    animation: ani9,
    trigger: "#section9",
    start: "top top",
    end: "+=4000",
    scrub: true,
    pin: true,
    markers: false,
    anticipatePin: 1
});

3. 마무리

예제 1번의 애니메이션 기본과 예제 2번의 핀 애니메이션을 이용하면 패럴랙스의 대부분 효과를 연출할 수 있습니다. 기본기를 배웠으니 이제는 여러분들이 응용해서 더 멋있는 효과를 만들 차례입니다. 그 전에 조금더 다양한 효과를 더 배우고 멋있는 사이트를 만들어 보도록 합시다. 수고하셨습니다. 🥺


예제 목록

댓글