19 gsap
1. gsap
gsap공식사이트 gsap코드펜 gsap치트시트 gsap의 모든 기능이 있는 cdn 링크
웹 브라우저에서 애니메이션을 구현하기 위한 자바스크립트 라이브러리 기존 CSS나 순수 자바스크립트보다 탁월한 퍼포먼스를 발휘할 수 있도록 최적화 된 애니메이션 전용 라이브러리이며, HTML5와 Flash 두 개의 플랫폼에서 사용할 수 있다. GreenSock Engaging the Internet GASP은 애니메이션을 쉽게 적용할 수 있는 라이브러리로 탁월한 퍼포먼스로 알려져 있다.
-
gsap.to() 메소드를 이용해 자연스러운 움직임을 만들 수 있다
-
다양한 종류의 애니메이션을 제공하며 필요에 따라 플러그인 형태로 추가하여 사용할수 있다. (일부유료)
-
기본 기능만을 사용할때는 gsap.js 혹은 gsap.min.js 을 사용한다.
-
문법은 camelCase 를 사용한다. ex) background-color (x), backgroundColor (O)
2. 설치
2.1. cdn
1<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>2.2. npm
1npm install gsap2.3. css transform과 gsap 속성 비교
| gsap | css |
|---|---|
x: 100 | transform: translateX(100px) |
y: 100 | transform: translateY(100px) |
rotation: 360 | transform: rotate(360deg) |
rotationX: 360 | transform: rotateX(360deg) |
rotationY: 360 | transform: rotateY(360deg) |
skewX: 45 | transform: skewX(45px) |
skewY: 45 | transform: skewY(45deg) |
scale: 2 | transform: scale(2,2) |
scaleX: 2 | transform: scaleX(2) |
scaleY: 2 | transform: scaleY(2) |
xPercent: -50 | transform: translateX(-50%) |
yPercent: -50 | transform: translateY(-50%) |
2.4. 기타속성
| 속성 | 설명 |
|---|---|
| delay | 애니메이션 시작 전 지연 시간 지정 |
| repeat | 반복횟수 지정, -1(무한반복) |
| repeatDelay | 반복 전 지연 시간 지정 |
| yoyo | true, 반복할 때 뒤로 되돌리기 |
| onComplete | 애니메이션이 끝났을 때 호출할 콜백함수 지정 |
| onUpdate | 애니메이션이 업데이트될 때마다 호출한 콜백함수 지정 |
| ease | 가속도 (power1, elastic, bounce,…) |
| stagger | 타겟과 요소 애니메이션을 위한 시작 시간 지정 |
3. Tween
3.1. 예제
애니메이션 작업을 수행한다. 타겟(애니메이션을 적용하려는 개체), 지속 시간 및 애니메이션을 적용하려는 속성을 입력하면 간단히 애니메이션을 완성해준다. 트윈을 생성하는 매서드는 3종류가 있다.
- gsap.to()
- gsap.from()
- gsap.fromTo()
이동
css로 transform: translateX(200px) 과 같은 효과를 주며 duration은 미작성시 0.5초 이다
1<head>2 <style>3 .box {4 margin: 15px;5 }6 .box1 {7 width: 100px;8 height: 100px;9 background: orange;10 }11 .box2 {12 width: 200px;13 height: 100px;14 background: greenyellow;15 }16 .box3 {17 width: 100px;18 height: 200px;19 background: tomato;20 }21 </style>22</head>23
24<body>25 <div class="box box1"></div>26 <div class="box box2"></div>27 <div class="box box3"></div>28
29 <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/gsap.min.js"></script>30 <script>31 gsap.to('.box', {32 x: 200,33 });34 </script>35</body>이동+회전
기본구조는 01과 같다.스크립트만 수정한다.
1gsap.to('.box1', { duration: 3, x: 200, opacity: 0.2, ease: 'steps(10)', delay: 2 });2gsap.to('.box2', { duration: 3, x: 200, rotate: 720, scale: 1.3 });3gsap.to('.box3', { duration: 3, x: 200, ease: 'elastic', backgroundColor: 'red', width: 300, fontSize: 60 });from 으로 변경하면 에니메이션이 반전되어 실행된다.
1gsap.from('.box1', { duration: 3, x: 200, opacity: 0.2, ease: 'steps(10)', delay: 2 });2gsap.from('.box2', { duration: 3, x: 200, rotate: 720, scale: 1.3 });3gsap.from('.box3', { duration: 3, x: 200, ease: 'elastic', backgroundColor: 'red', width: 300, fontSize: 60 });03. 이동+투명도 조절
“h2” 요소에 1초 동안 투명도를 30%로 애니메이션 적용하고 “.orange” 요소는 2초 동안 x축으로 300pixel 이동하고 “.grey”는 Y축으로 300px 이동. “.green” 요 소는 3초 동안 360도 회전하고 크기를 50%로 축소하는 애니메이션 을 적용한다
1<h2 class="title">gsap.to() Basic Usage</h2>2<div class="box orange"></div>3<div class="box grey"></div>4<div class="box green"></div>1body {2 margin: 10px;3}4.box {5 width: 100px;6 height: 100px;7}8.orange {9 background: orange;10}11.grey {12 background: grey;13}14.green {15 background: green;16}1gsap.to('h2.title', { duration: 1, opacity: 0.3 });2gsap.to('.orange', { duration: 2, x: 300 });3gsap.to('.grey', { duration: 2, y: 300 });4gsap.to('.green', { duration: 3, rotation: 360, scale: 0.5 });4. easing
참고
easing 은 가속도를 설정할수 있는 속성이다. 사용할수 있는 값은 아래와 같다. none, power1, power2, power3, power4, back, elastic, bounce, rough, slow, steps, circ, expo, sine []공식문서](https://gsap.com/docs/v3/Eases/)
4.1. 예제
04. 가속도
1<section id="title">2 <div class="box box1">none</div>3 <div class="box box2">power1</div>4 <div class="box box3">power2</div>5 <div class="box box4">power3</div>6 <div class="box box5">power4</div>7 <div class="box box6">back</div>8 <div class="box box7">elastic</div>9 <div class="box box8">bounce</div>10 <div class="box box9">rough</div>11 <div class="box box10">slow</div>12 <div class="box box11">steps</div>13 <div class="box box12">circ</div>14 <div class="box box13">expo</div>15 <div class="box box14">sine</div>16</section>1* {2 margin: 0;3 padding: 0;4 box-sizing: border-box;5}6
7#title {8 width: 700px;9 margin: 100px auto;10}11
12.box {13 background-color: purple;14 width: 50px;15 height: 50px;16 border-radius: 50%;17 text-align: center;18 line-height: 50px;19 font-size: 12px;20}1gsap.to('.box1', { x: 600, ease: 'none', duration: 5 });2gsap.to('.box2', { x: 600, ease: 'power1', duration: 5 });3gsap.to('.box3', { x: 600, ease: 'power2', duration: 5 });4gsap.to('.box4', { x: 600, ease: 'power3', duration: 5 });5gsap.to('.box5', { x: 600, ease: 'power4', duration: 5 });6gsap.to('.box6', { x: 600, ease: 'back', duration: 5 });7gsap.to('.box7', { x: 600, ease: 'elastic', duration: 5 });8gsap.to('.box8', { x: 600, ease: 'bounce', duration: 5 });9gsap.to('.box9', { x: 600, ease: 'rough', duration: 5 });10gsap.to('.box10', { x: 600, ease: 'slow', duration: 5 });11gsap.to('.box11', { x: 600, ease: 'steps(5)', duration: 5 });12gsap.to('.box12', { x: 600, ease: 'circ', duration: 5 });13gsap.to('.box13', { x: 600, ease: 'expo', duration: 5 });14gsap.to('.box14', { x: 600, ease: 'sine', duration: 5 });5. gsap.timeline()
타임라인 메서드는 애니메이션의 시간을 지정할수 있어 다양한 모션 구현시 편리하다.
5.1. 예제
04. timeline
1<div class="one"></div>2<div class="two"></div>3<div class="three"></div>4<div class="four"></div>1* {2 margin: 0;3 padding: 0;4 box-sizing: border-box;5}6
7body {8 overflow: hidden;9}10
11div {12 width: 100px;13 height: 100px;14}15
16.one {17 background: red;18}19
20.two {21 background: green;22}23
24.three {25 background: blue;26}27
28.four {29 background: pink;30}1let tl = gsap.timeline();2//total duration:3tl.to('.one', { duration: 2, x: 500 })4 //2초 동안 x축으로 500px5
6 .to('.two', { duration: 3, x: 500 }, 1)7 // 1초 후 3초동안 x축으로 500px(1은 absolute값)8
9 .to('.three', { duration: 1, x: 500 }, '<')10 //1초 후 1초동안 x축으로 500px(<은 이전 target의 timeline을 따라감)11
12 .to('.four', { duration: 1, x: 500 }, '<0.5');13//1.5초후 1초동은 x축으로 500px(<0.5를 추가하면 이전 target보다 0.5초 뒤 실행05. parallax
참고
timeline 을 활용하여 다른 클래스의 요소들에 시차애니메이션을 적용한다.
변수 t1을 선언하고 기본값으로 1초를 delay로 할당한다.
1<section id="title">2 <div class="container">3 <h1 class="animation1">JavaScript GSAP Library Animation</h1>4 <p class="animation1">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Tempore, dolore.</p>5 <a class="animation1" href="#">Button</a>6 </div>7</section>8<section>9 <div class="box box1"></div>10 <div class="box box2"></div>11</section>12<section id="thumbnail">13 <img src="http://placekitten.com/200/300" />14</section>1* {2 margin: 0;3 padding: 0;4 box-sizing: border-box;5}6
7body {8 overflow: hidden;9}10
11#title {12 width: 700px;13 margin: 100px auto;14}15
16.container {17 padding: 20px;18}19
20h1 {21 margin-bottom: 20px;22}23
24p {25 margin-bottom: 15px;26}27
28a {29 display: block;30 width: 100px;31 padding: 10px;32 background-color: blueviolet;33 text-decoration: none;34 color: white;35 text-align: center;36 border-radius: 10px;37}38
39#thumbnail {40 width: 420px;41 height: 300px;42 position: absolute;43 right: 0;44 bottom: 0;45}46.box {47 width: 100px;48 height: 100px;49}50
51.box1 {52 background-color: red;53}54
55.box2 {56 background-color: blue;57}1let t1 = gsap.timeline({ defaults: { duration: 1 } });2//defaults라는 특수 속성은 모든 하위 트윈과 타임라인에서 값을 상속할 수 있다.3//애니메이션을 1초동안 진행하는 기본값 설정4
5t1.from('h1', { y: -50, opacity: 0 })6 .from('p', { y: -50, opacity: 0 }, '-=0.5') // 타임라인 종료 0.5초 전 (오버랩)7 .from('a', { y: -50, opacity: 0 }, '+=1') // 타임라인 종료 1초 후 (갭)8 .from('img', { y: 200, opacity: 0 }, '3'); // 타임라인 시작으로부터 3초 후 (절대적)9 .from('.box1', { x: 200, opacity: 0 }, "<") // 이전 트윈 타임라인 시작지점10 .to('.box2', { rotate: 360, x:800, opacity: 1}, ">") // 이전 트윈 타임라인 종료지점06. 클릭시 반대로 진행
문서의 구조와 스타일은 이전과 동일
1let t1 = gsap.timeline({ defaults: { duration: 1 } });2
3t1.from('h1', { y: -50, opacity: 0 })4 .from('p', { y: -50, opacity: 0 }, '-=0.5') // 타임라인 종료 0.5초 전 (오버랩)5 .from('a', { y: -50, opacity: 0 }, '-=0.5') // 타임라인 종료 0.5초 전 (오버랩)6 .from('img', { y: 200, opacity: 0 }, '-=0.5')7 .from('.box1', { x: 200, opacity: 0 }, '<') // 이전 트윈 타임라인 시작지점8 .to('.box2', { rotate: 360, x: 800, opacity: 1 }, '>'); // 이전 트윈 타임라인 종료지점9
10document.getElementById('cta').addEventListener('click', (e) => {11 e.preventDefault();12 t1.reversed() ? t1.play() : t1.reverse();13 //타임라인의 진행상태가 반전 되었을경우 시작하고 아닐경우 반전14});핸들링예제
1<!DOCTYPE html>2<html lang="ko">3 <head>4 <meta charset="UTF-8" />5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />6 <title>Document</title>7 <style>8 .box {9 width: 100px;10 height: 100px;11 }12
13 .box1 {14 background-color: red;15 }16 </style>17 </head>18
19 <body>20 <div class="box box1"></div>21 <div class="nav">22 <button id="play">시작</button>23 <button id="pause">정지</button>24 <button id="resume">재개</button>25 <button id="reverse">반전</button>26 <button id="restart">재시작</button>27 </div>28 <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.8.0/gsap.min.js"></script>29 <script>30 const tween = gsap.to(".box1", { duration: 8, x: 400, width: 400, paused: true, });31
32 document.querySelector("#play").onclick = function () { return tween.play(); };33 document.querySelector("#pause").onclick = function () { return tween.pause(); };34 document.querySelector("#resume").onclick = function () { return tween.resume(); };35 document.querySelector("#reverse").onclick = function () { return tween.reverse(); };36 document.querySelector("#restart").onclick = function () { return tween.restart(); }.37
38 //resume 애니메이션 멈춘곳 부터 재생을 다시 시작39 </script>40 </body>41</html>6. scrollTrigger
스크롤트리거는 gsap 의 플러그인 으로 추가설치 해야한다.
6.1. 주요속성
| 속성 | 설명 |
|---|---|
| trigger | 애니메이션이 시작되거나 끝나는 지점을 감지하는 요소이다. |
| start | 애니메이션 트리거가 시작되는 스크롤 위치이다. 형식은 “시작점 끝점”이다. |
| end | 애니메이션 트리거가 끝나는 스크롤 위치이다. 형식은 “시작점 끝점”이다. |
| scrub | 스크롤 위치에 따라 애니메이션이 연동되어 재생되는지 여부이다. true로 설정하면 스크롤에 따라 애니메이션이 재생된다. |
| pin | 특정 요소를 스크롤 동안 고정시키는 기능이다. true로 설정하면, 지정된 요소가 고정된다. |
| markers | 시작점, 끝점, 그리고 트리거 요소의 위치를 화면에 표시하는 개발 도구이다. true로 설정하면 마커가 표시된다. |
| toggleActions | 트리거 지점마다 애니메이션의 실행제어 • onEnter: scroll-start ~ scroll-end 사이 • onLeave : scroll-end 부분을 넘어갈 때 • onEnterBack : scroll-start ~ scroll-end 사이 재 진입시• onLeaveBack : scroll-end부분 재 퇴장시 • 액션값: play, pause, resume, reset, restart, complete, reverse(중지된 곳부터 재생), none; • 기본값 : toggleActions:play none none none |
| id | ScrollTrigger 인스턴스에 고유 식별자를 부여한다. 여러 ScrollTrigger를 관리할 때 유용하다. |
| onEnter | 스크롤이 트리거 시작 지점을 지날 때 실행할 콜백 함수이다. |
| onLeave | 스크롤이 트리거 끝 지점을 벗어날 때 실행할 콜백 함수이다. |
| onEnterBack | 스크롤이 트리거 끝 지점에서 시작 지점으로 돌아올 때 실행할 콜백 함수이다. |
| onLeaveBack | 스크롤이 트리거 시작 지점을 다시 벗어날 때 실행할 콜백 함수이다. |
| pinSpacing | pin이 활성화된 요소의 공간을 어떻게 처리할지 정의한다. “margin” 또는 “padding” 값을 사용할 수 있다. |
6.2. 주요메서드
| 메서드 | 설명 |
|---|---|
| create | 새로운 ScrollTrigger 인스턴스를 생성한다. 인스턴스는 개별 애니메이션의 스크롤 트리거 설정을 관리한다. |
| kill | ScrollTrigger 인스턴스를 제거한다. 인스턴스에 의해 생성된 변경사항들도 함께 제거된다 |
6.3. 예제
1<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>2<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>07. 스크롤트리거 기본
1<section></section>2<section>3 <div class="box"></div>4</section>5<section></section>1.box {2 width: 200px;3 height: 200px;4 background: pink;5}6
7section {8 height: 100vh;9}- 섹션별 배경색 추가
1const bg = document.querySelectorAll('section');2const colors = {3 r: 255,4 g: 255,5 b: 255,6};7
8bg.forEach((o, i) => {9 o.style.backgroundColor = `rgb(${colors.r - i * 100},${colors.g - i * 50},${colors.b - i * 50})`;10});- 스크롤트리거 작성
1gsap.to('.box', {2 scrollTrigger: '.box',3 x: '50vw',4});08. 스크롤트리거-애니메이트
- html
1<main class="cont">2 <section class="sec1 item">3 <h2 class="num">01</h2>4 <div class="box"></div>5 </section>6 <!-- //.sec1 -->7 <section class="sec2 item">8 <h2 class="num">02</h2>9 <div class="box"></div>10 </section>11 <!-- //.sec2 -->12 <section class="sec3 item">13 <h2 class="num">03</h2>14 <div class="box"></div>15 </section>16 <!-- //.sec3 -->17 <section class="sec4 item">18 <h2 class="num">04</h2>19 <div class="box"></div>20 </section>21 <!-- //.sec4 -->22 <section class="sec5 item">23 <h2 class="num">05</h2>24 <div class="box"></div>25 </section>26 <!-- //.sec5 -->27 <section class="sec6 item">28 <h2 class="num">06</h2>29 <div class="box"></div>30 </section>31 <!-- //.sec6 -->32 <section class="sec7 item">33 <h2 class="num">07</h2>34 <div class="box box7"></div>35 </section>36 <!-- //.sec7 -->37 <section class="sec8 item">38 <h2 class="num">08</h2>39 <div class="box"></div>40 </section>41 <!-- //.sec8 -->42</main>- css
1@import url(https://qwerewqwerew.github.io/source/style/reset.css);2.cont {3 overflow: hidden;4}5.item {6 width: clamp(50%, 100%, 100%);7 height: 100vh;8 position: relative;9 display: flex;10 align-items: center;11 justify-content: center;12 gap: 5vw;13}14.item:nth-child(2n) {15 background-color: gray;16}17.num {18 position: absolute;19 right: 20px;20 bottom: 20px;21 font-size: 5vw;22 line-height: 1;23}24.box {25 width: 10vw;26 height: 10vw;27 background-color: #ff1515;28 background-size: cover;29 background-position: center;30}31.active.box {32 filter: hue-rotate(100deg);33}- js
1const box1 = document.querySelector('.sec1 .box');2const box2 = document.querySelector('.sec2 .box');3const box3 = document.querySelector('.sec3 .box');4const box4 = document.querySelector('.sec4 .box');5const box5 = document.querySelector('.sec5 .box');6const box6 = document.querySelector('.sec6 .box');7const box7 = document.querySelector('.sec7 .box');8const box8 = document.querySelector('.sec8 .box');9const box9 = document.querySelector('.sec9 .box');- jq
1const box1 = $('.sec1 .box');2const box2 = $('.sec2 .box');3const box3 = $('.sec3 .box');4const box4 = $('.sec4 .box');5const box5 = $('.sec5 .box');6const box6 = $('.sec6 .box');7const box7 = $('.sec7 .box');8const box8 = $('.sec8 .box');9const box9 = $('.sec9 .box');- gsap
1// 012gsap.to(box1, {3 duration: 2,4 x: 500,5 borderRadius: 100,6 rotation: 360,7});8
9// 02 : trigger10gsap.to(box2, {11 duration: 2,12 x: 500,13 rotation: 360,14 borderRadius: 100,15 backgroundColor: 'yellow',16 scrollTrigger: {17 trigger: box2,18 },19});20
21// 03 : toggleActions22gsap.to(box3, {23 duration: 2,24 x: 500,25 rotation: 360,26 borderRadius: 100,27 scrollTrigger: {28 trigger: box3,29 toggleActions: 'play none reverse none',30 },31});32
33// 04 : start, end34gsap.to(box4, {35 duration: 2,36 x: 500,37 rotation: 360,38 borderRadius: 100,39 scrollTrigger: {40 trigger: box4,41 start: 'top 50%',42 end: 'bottom 20%',43 toggleActions: 'play none reverse none',44 markers: true,45 },46});47
48// 05 : scrub49gsap.to(box5, {50 duration: 2,51 x: 500,52 rotation: 360,53 borderRadius: 100,54
55 scrollTrigger: {56 trigger: box5,57 start: 'top 50%',58 end: 'bottom 20%',59 scrub: true, //true, 1, 2...60 markers: false,61 },62});63
64// 06 : pin65gsap.to(box6, {66 duration: 2,67 x: 500,68 rotation: 360,69 borderRadius: 100,70
71 scrollTrigger: {72 trigger: box6,73 start: 'top 50%',74 end: 'bottom 200px',75 scrub: true,76 pin: true,77 markers: false,78 },79});80
81// 07 : toggleClass82gsap.to(box7, {83 duration: 2,84 x: 500,85 rotation: 360,86 borderRadius: 100,87 scrollTrigger: {88 trigger: box7,89 start: 'top center',90 end: 'bottom 20%',91 scrub: true,92 toggleClass: 'active',93 markers: false,94 id: 'box7',95 },96});97
98// 08 : callback99gsap.to(box8, {100 duration: 2,101 x: 500,102 rotation: 360,103 borderRadius: 100,104
105 scrollTrigger: {106 trigger: box8,107 start: 'top center',108 end: 'bottom 20%',109 scrub: true,110 markers: true,111 // onEnter: () => {console.log("onEnter")},112 // onLeave: () => {console.log("onLeave")},113 // onEnterBack: () => {console.log("onEnterBack")},114 // onLeaveBack: () => {console.log("onLeaveBack")},115 onUpdate: (self) => {116 console.log('onUpdate', self.progress.toFixed(3));117 },118 onToggle: (self) => {119 console.log('onToggle', self.isActive);120 myAni();121 },122 },123});124function myAni() {125 const txt = '<span>1</span>';126 box8.parent().append(txt);127}09. 스크롤트리거-핀
- html 08번 예제와 유사하나 일부 요소가 추가된다.
1<section class="sec2 item">2 <h2 class="num">02</h2>3 <div class="box i1"></div>4 <div class="box i2"></div>5 <div class="box i3"></div>6</section>7<!-- //.sec2 -->8<section class="sec3 item">9 <h2 class="num">03</h2>10 <div class="box"></div>11 <div class="box"></div>12 <div class="box"></div>13 <div class="box"></div>14 <div class="box"></div>15</section>16<!-- //.sec3 -->17<section class="sec4 item">18 <h2 class="num">04</h2>19 <div class="box"></div>20</section>21<!-- //.sec4 -->22<section class="sec5 item">23 <h2 class="num">05</h2>24 <div class="text t1">Lorem</div>25 <div class="text t2">Lorem</div>26 <div class="text t3">Lorem</div>27 <div class="text t4">Lorem</div>28</section>29<!-- //.sec5 -->30<section class="sec6 item">31 <h2 class="num">06</h2>32 <div class="text">Lorem</div>33</section>34<!-- //.sec6 -->35<section class="sec7 item">36 <h2 class="num">07</h2>37 <div class="text t1">Lorem</div>38 <div class="text t2">Ipsum</div>39 <div class="text t3">Gsap</div>40 <div class="text t4">HTML</div>41 <div class="text t5">CSS</div>42 <div class="text t6">Javascript</div>43 <div class="text t7">Jquery</div>44</section>45<!-- //.sec7 -->46<section class="sec8 item">47 <h2 class="num">08</h2>48 <div class="text t1">Lorem</div>49 <div class="text t2">Ipsum</div>50 <div class="text t3">Gsap</div>51 <div class="box"></div>52</section>53<!-- //.sec8 -->54<section class="sec9 item">55 <h2 class="num">09</h2>56 <div class="box"></div>57</section>58<!-- //.sec9 -->- css 추가
1.sec4 .box,2.sec9 .box {3 background: url(http://qwerew.cafe24.com/images/1.jpg) no-repeat center center/100%;4}5.text {6 font-size: 10rem;7}8.sec5.item {9 flex-direction: column;10}11.sec7 .text {12 position: absolute;13 left: 50%;14 top: 50%;15 transform: translate(-50%, -50%);16 background-color: #fff;17 width: 100%;18 padding: 2rem;19 text-align: center;20}- js
1//01 : 이미지 애니메이션 주기2const ani1 = gsap.timeline();3ani1.to('.sec1 .box', { rotation: 450, scale: 0, borderRadius: 200 }).to('.sec1 .box', { rotation: 360 * 5, scale: 1, borderRadius: 20 });4ScrollTrigger.create({5 animation: ani1,6 trigger: '.sec1',7 start: 'top top',8 end: '+=2000', //시작 부분부터 2000px까지 스크롤 한 후종료9 scrub: true,10 pin: true, //화면고정 //true | 요소이름11 anticipatePin: 1,12 markers: true,13 id: 'sec1',14});15
16//02 : 순차 나타나기17const ani2 = gsap.timeline();18ani2.from('.sec2 .i1', { y: 200, autoAlpha: 0 }).from('.sec2 .i2', { y: 100, autoAlpha: 0 }).from('.sec2 .i3', { y: -100, autoAlpha: 0 });19
20ScrollTrigger.create({21 animation: ani2,22 trigger: '.sec2',23 start: 'top top',24 end: '+=2000',25 scrub: true,26 pin: true,27 anticipatePin: 1,28 markers: true,29});30
31//03 : 랜덤32const ani3 = gsap.timeline();33ani3.from('.sec3 .box', {34 y: -300,35 scale: 0.5,36 autoAlpha: 0,37 ease: 'back.out(4)',38 //stagger: 0.3, // 0.3초 간격으로 실행39 stagger: {40 amount: 3, //총 3초 동안 무작위41 from: 'random',42 },43});44
45ScrollTrigger.create({46 animation: ani3,47 trigger: '.sec3',48 start: 'top top',49 end: '+=2000',50 scrub: true,51 pin: true,52 anticipatePin: 1,53 markers: true,54});55
56//04 : 이미지 축소하기57const ani4 = gsap.timeline();58ani4.from('.sec4 .box', {59 autoAlpha: 0,60 scale: 5,61 width: '100vw',62 height: '100vh',63});64
65ScrollTrigger.create({66 animation: ani4,67 trigger: '.sec4',68 start: 'top top',69 end: '+=3000',70 scrub: true,71 pin: true,72 anticipatePin: 1, //1초전 //핀이 시작되기 직전에 애니메이션을 미리 시작해서 예상치 못한 끊김 방지73 markers: true,74});75
76//05 : 텍스트 애니메이션77const ani5 = gsap.timeline();78
79//"-=1" 1초전 시작80ani5.to('.sec5 .t1', { xPercent: 300 }).to('.sec5 .t2', { xPercent: -300 }, '-=1').to('.sec5 .t3', { xPercent: 300 }).to('.sec5 .t4', { xPercent: -300 });81
82//'text' 그룹핑기능 함께 동시실행83
84//ani5.to('.sec5 .t1', { xPercent: 300 }).to('.sec5 .t2', { xPercent: -300 }, 'text').to('.sec5 .t3', { xPercent: 300 }, 'text').to('.sec5 .t4', { xPercent: -300 }, 'text');85
86ScrollTrigger.create({87 animation: ani5,88 trigger: '.sec5',89 start: 'top top',90 end: '+=3000',91 scrub: true,92 pin: true,93 anticipatePin: 1,94 markers: true,95});96
97//06 : 텍스트 확대하기98const ani6 = gsap.timeline();99ani6.to('.sec6 .text', { scale: 60, duration: 2 }).to('.sec6 .text', { autoAlpha: 0 });100
101ScrollTrigger.create({102 animation: ani6,103 trigger: '.sec6',104 start: 'top top',105 end: '+=4000',106 scrub: true,107 pin: true,108 anticipatePin: 1,109 markers: true,110});111
112//07 : 텍스트 제자리 애니메이션113const ani7 = gsap.timeline();114ani7.from('.sec7 .t1', { autoAlpha: 0, duration: 1, y: 50 }, '+=1').from('.sec7 .t2', { autoAlpha: 0, duration: 1, y: 50 }, '+=1').from('.sec7 .t3', { autoAlpha: 0, duration: 1, y: 50 }, '+=1').from('.sec7 .t4', { autoAlpha: 0, duration: 1, y: 50 }, '+=1').from('.sec7 .t5', { autoAlpha: 0, duration: 1, y: 50 }, '+=1').from('.sec7 .t6', { autoAlpha: 0, duration: 1, y: 50 }, '+=1').from('.sec7 .t7', { autoAlpha: 0, duration: 1, y: 50 }, '+=1');115
116ScrollTrigger.create({117 animation: ani7,118 trigger: '.sec7',119 start: 'top top',120 end: '+=6000',121 scrub: true,122 pin: true,123 anticipatePin: 1,124 markers: true,125});126
127//08 : 텍스트 애니메이션128const ani8 = gsap.timeline();129ani8130 .from('.sec8 .t1', { x: innerWidth * 1 })131 .from('.sec8 .t2', { x: innerWidth * -1 })132 .from('.sec8 .t3', { x: innerWidth * 1 })133 .from('.sec8 .box', { x: innerWidth * 1, rotation: 360, scale: 5.5 });134
135ScrollTrigger.create({136 animation: ani8,137 trigger: '.sec8',138 start: 'top top',139 end: '+=4000',140 scrub: true,141 pin: true,142 anticipatePin: 1,143 markers: true,144});145
146//09 : 이미지 확대하기147const ani9 = gsap.timeline();148ani9.to('.sec9 .box', { scale: 13 }).to('.sec9 .box', { autoAlpha: 0 });149
150ScrollTrigger.create({151 animation: ani9,152 trigger: '.sec9',153 start: 'top top',154 end: '+=4000',155 scrub: true,156 pin: true,157 markers: false,158 anticipatePin: 1,159});anticipatePin : 핀이 시작되기 전 애니메이션을 미리 시작해서 예상치 못한 끊김 방지 (1은 1초전 시작)
10. 스크롤트리거-핀(섹션)
- html 작성
1<main class="cont">2 <!-- section.item.sec$*5>h2.num+.box -->3 <section class="item sec1">4 <h2 class="num">1</h2>5 <div class="box"></div>6 </section>7 <section class="item sec2">8 <h2 class="num">2</h2>9 <div class="box"></div>10 </section>11 <section class="item sec3">12 <h2 class="num">3</h2>13 <div class="box"></div>14 </section>15 <section class="item sec4">16 <h2 class="num">4</h2>17 <div class="box"></div>18 </section>19 <section class="item sec5">20 <h2 class="num">5</h2>21 <div class="box"></div>22 </section>23</main>- css 추가
1.box {2 position: absolute;3 left: 0;4 top: 0;5 width: 100%;6 height: 100vh;7 background-color: #fff;8 background-size: cover;9 background-position: center;10}11.box::after {12 content: '';13 position: absolute;14 left: 0;15 top: 0;16 width: 100%;17 height: 100vh;18 background-color: rgba(0, 0, 0, 0.7);19 z-index: 1;20}21.num {22 position: absolute;23 font-size: 10vw;24 z-index: 10;25 color: #fff;26 left: 50%;27 top: 50%;28 transform: translate(-50%, -50%);29}30.sec1 .box {31 background-image: url(http://qwerew.cafe24.com/images/1.jpg);32}33.sec2 .box {34 background-image: url(http://qwerew.cafe24.com/images/2.jpg);35}36.sec3 .box {37 background-image: url(http://qwerew.cafe24.com/images/3.jpg);38}39.sec4 .box {40 background-image: url(http://qwerew.cafe24.com/images/4.jpg);41}42.sec5 .box {43 background-image: url(http://qwerew.cafe24.com/images/5.jpg);44}- js
1// 01. 한개 섹션 고정시키기2
3function fn1() {4 const panel = document.querySelector('.sec5');5
6 ScrollTrigger.create({7 trigger: panel,8 start: 'top top',9 pin: true,10 pinSpacing: false,11 });12}13// 02. 여러개 섹션 고정시키기14function fn2() {15 gsap.utils.toArray('.box').forEach((panel, i) => {16 ScrollTrigger.create({17 trigger: panel,18 start: 'top top',19 pin: true,20 pinSpacing: false,21 });22 });23}24// 03. 스냅 고정25function fn3() {26 let panels = gsap.utils.toArray('.box');27 let tops = panels.map((panel) => ScrollTrigger.create({ trigger: panel, start: 'top top' }));28 console.log(tops);29 panels.forEach((panel, i) => {30 ScrollTrigger.create({31 trigger: panel,32 start: () => (panel.offsetHeight < window.innerHeight ? 'top top' : 'bottom bottom'),33 pin: true,34 pinSpacing: false,35 });36 });37
38 ScrollTrigger.create({39 snap: {40 snapTo: (progress, self) => {41 let panelStarts = tops.map((st) => st.start),42 snapScroll = gsap.utils.snap(panelStarts, self.scroll());43 return gsap.utils.normalize(0, ScrollTrigger.maxScroll(window), snapScroll);44 },45 duration: 0.5,46 },47 });48}49fn3();11. 스크롤트리거-패럴렉스
- html
1<main class="cont">2 <!-- section.item.sec$*9>h2.num+.box -->3 <section class="item sec1">4 <h2 class="num">1</h2>5 <div class="img_wrap">6 <div class="img"></div>7 </div>8 <div class="desc">Lorem ipsum dolor sit, amet</div>9 </section>10 <section class="item sec2">11 <h2 class="num">2</h2>12 <div class="img_wrap">13 <div class="img"></div>14 </div>15 <div class="desc">Lorem ipsum dolor sit, amet</div>16 </section>17 <section class="item sec3">18 <h2 class="num reveal">3</h2>19 <div class="img_wrap">20 <div class="img "></div>21 </div>22 <div class="desc reveal ltr" data-delay="0.8">Lorem ipsum dolor sit, amet</div>23 <div class="desc reveal ttb" data-delay="0.3">Lorem ipsum dolor sit, amet</div>24 </section>25 <section class="item sec4">26 <h2 class="num">4</h2>27 <div class="img_wrap reveal">28 <div class="img"></div>29 </div>30 <div class="desc reveal btt" data-delay="0.5">Lorem ipsum dolor sit, amet</div>31 </section>32</main>- css
1section.item {2 padding-top: 5vw;3 margin: 10vw 0;4 margin-right: 0;5 text-align: left;6}7.img_wrap {8 position: relative;9 width: 100%;10 overflow: hidden;11 /* 1080/1920*100 */12 padding-bottom: 50%;13}14.img {15 position: absolute;16 filter: grayscale(100%);17 transition: all 1s;18 width: 100%;19 height: 100%;20 background-repeat: no-repeat;21 background-position: center center;22 background-size: cover;23}24.img_wrap:hover .img {25 filter: grayscale(0%);26 transform: scale(1.3);27}28.num {29 position: absolute;30 font-size: 10vw;31 z-index: 10;32 color: #fff;33 left: 50%;34 top: 50%;35 transform: translate(-50%, -50%);36}37.desc {38 font-size: 5vw;39 font-weight: 900;40 margin-left: -50%;41 margin-top: 50%;42 z-index: 100;43 position: relative;44 -webkit-text-fill-color: #0000802d;45 -webkit-text-stroke: 1px #000080;46 text-stroke: 1px 000080;47}48.desc:before {49 content: 'Lorem ipsum dolor sit, amet';50 position: absolute;51 filter: drop-shadow(-15px -10px 10px rgba(28, 33, 106, 0.5)) drop-shadow(10px 10px 10px rgba(13, 16, 65, 0.8)) drop-shadow(20px 20px 40px rgba(9, 11, 41, 0.8));52}53.ttb {54 margin-top: 0%;55}56.sec1 .img {57 background-image: url(http://qwerew.cafe24.com/images/1.jpg);58}59.sec2 .img {60 background-image: url(http://qwerew.cafe24.com/images/2.jpg);61}62.sec3 .img {63 background-image: url(http://qwerew.cafe24.com/images/3.jpg);64}65.sec4 .img {66 background-image: url(http://qwerew.cafe24.com/images/4.jpg);67}- js
1//01. 패럴렉스1개2const fn1 = () => {3 gsap.to('.desc', {4 yPercent: -100,5 ease: 'none',6 duration: 0.5,7 scrollTrigger: {8 trigger: '.desc',9 start: 'top bottom',10 end: 'bottom top',11 markers: true,12 scrub: true,13 },14 });15};16
17//02. 패럴렉스여러개18const fn = () => {19 gsap.utils.toArray('.desc').forEach((item) => {20 gsap.to(item, {21 yPercent: -200,22 ease: 'none',23 duration: 0.5,24 scrollTrigger: {25 trigger: item,26 start: 'top bottom',27 end: 'bottom top',28 markers: false,29 scrub: 0.5,30 },31 });32 });33};34fn();35
36//03. 나타나기37const hide = (item) => {38 gsap.set(item, { autoAlpha: 0 });39};40
41const animate = (item) => {42 let x = 0;43 let y = 0;44 let delay = item.dataset.delay;45
46 if (item.classList.contains('ltr')) {47 (x = -100), (y = 0);48 } else if (item.classList.contains('btt')) {49 (x = 0), (y = 100);50 } else if (item.classList.contains('ttb')) {51 (x = 0), (y = -100);52 } else {53 (x = 100), (y = 0);54 }55
56 gsap.fromTo(item, { autoAlpha: 0, x: x, y: y }, { autoAlpha: 1, x: 0, y: 0, delay: delay, duration: 1.25, overwrite: 'auto', ease: 'expo' });57};58
59gsap.utils.toArray('.reveal').forEach((item) => {60 hide(item);61 ScrollTrigger.create({62 trigger: item,63 start: 'top bottom',64 end: 'bottom top',65 markers: false,66 onEnter: () => {67 animate(item);68 },69 });70});12. 스크롤트리거-부드러운 섹션이동(scrollTo)
- html
1<!-- nav>ul>(li>a)*9 -->2<nav>3 <h1>스크롤효과</h1>4 <ul>5 <li><a href="#sec1">sec1</a></li>6 <li><a href="#sec2">sec2</a></li>7 <li><a href="#sec3">sec3</a></li>8 <li><a href="#sec4">sec4</a></li>9 <li><a href="#sec5">sec5</a></li>10 <li><a href="#sec6">sec6</a></li>11 <li><a href="#sec7">sec7</a></li>12 <li><a href="#sec8">sec8</a></li>13 <li><a href="#sec9">sec9</a></li>14 </ul>15</nav>16<main class="cont">17 <!-- section.item#sec$*9>h2.num+.box -->18 <section class="item" id="sec1">19 <h2 class="num">1</h2>20 <div class="box"></div>21 </section>22 <section class="item" id="sec2">23 <h2 class="num">2</h2>24 <div class="box"></div>25 </section>26 <section class="item" id="sec3">27 <h2 class="num">3</h2>28 <div class="box"></div>29 </section>30 <section class="item" id="sec4">31 <h2 class="num">4</h2>32 <div class="box"></div>33 </section>34 <section class="item" id="sec5">35 <h2 class="num">5</h2>36 <div class="box"></div>37 </section>38 <section class="item" id="sec6">39 <h2 class="num">6</h2>40 <div class="box"></div>41 </section>42 <section class="item" id="sec7">43 <h2 class="num">7</h2>44 <div class="box"></div>45 </section>46 <section class="item" id="sec8">47 <h2 class="num">8</h2>48 <div class="box"></div>49 </section>50 <section class="item" id="sec9">51 <h2 class="num">9</h2>52 <div class="box"></div>53 </section>54</main>- css
1nav {2 position: absolute;3 display: flex;4 gap: 2rem;5 max-width: 80%;6 margin: auto;7 z-index: 9999;8 top: 0;9 transition: all 1s;10 left: 0;11 background-color: gray;12}13nav.active {14 position: fixed;15 top: 0;16 background-color: rgba(128, 128, 128, 0);17}18nav.active ul {19 padding: 0rem 8rem;20 border-radius: 50px;21 background-color: rgba(19, 116, 226, 0.5);22}23nav ul {24 display: flex;25 gap: 2rem;26 padding: 0rem 2rem;27 border-radius: 0px;28 transition: all 1s;29}30nav li {31 width: calc(100% / 9);32 height: 10rem;33 line-height: 10rem;34}35nav li a {36 font-size: 1.8rem;37 color: white;38}39nav li a.active {40 color: red;41}- js
1let links = gsap.utils.toArray('nav ul li a');2
3links.forEach((link) => {4 let element = document.querySelector(link.getAttribute('href'));5 let linkST = ScrollTrigger.create({6 trigger: element,7 start: 'top top',8 });9
10 ScrollTrigger.create({11 trigger: element,12 start: 'top center',13 end: 'bottom center',14 onToggle: (self) => setActive(link),15 });16
17 link.addEventListener('click', (e) => {18 e.preventDefault();19 gsap.to(window, { duration: 1, scrollTo: linkST.start, overwrite: 'auto' });20 });21});22
23function setActive(link) {24 links.forEach((el) => el.classList.remove('active'));25 link.classList.add('active');26}27
28ScrollTrigger.create({29 start: 'top -80',30 end: 99999,31 toggleClass: {32 className: 'active',33 targets: 'nav',34 },35});7. 가로방향 스크롤
[]가로스크롤Forum]
gsap을 이용하여 횡스크롤을 제작해보자
cdn 추가
1<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>2<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
예제
13. 가로스크롤
- html
1<main class="cont">2 <h1>가로스크롤</h1>3 <section class="item" id="sec1">4 <h2 class="num">1</h2>5 </section>6 <section class="item" id="sec2">7 <h2 class="num">2</h2>8 </section>9 <section class="item" id="sec3">10 <h2 class="num">3</h2>11 <div class="box"></div>12 </section>13 <section class="item" id="sec4">14 <h2 class="num">4</h2>15 <div class="box"></div>16 </section>17 <section class="item" id="sec5">18 <h2 class="num">5</h2>19 <div class="box"></div>20 </section>21 <section class="item" id="sec6">22 <h2 class="num">6</h2>23 <div class="box"></div>24 </section>25 <section class="item" id="sec7">26 <h2 class="num">7</h2>27 <div class="box"></div>28 </section>29 <section class="item" id="sec8">30 <h2 class="num">8</h2>31 <div class="box"></div>32 </section>33 <section class="item" id="sec9">34 <h2 class="num">9</h2>35 <div class="box"></div>36 </section>37</main>- css
1main {2 overscroll-behavior: none;3 width: 900%;4 height: 100vh;5 display: flex;6 flex-wrap: nowrap;7}- js
1let sections = gsap.utils.toArray('section');2
3const move = gsap.to(sections, {4 xPercent: -100 * (sections.length - 1),5 ease: 'none',6 scrollTrigger: {7 trigger: 'main',8 pin: true,9 scrub: 1,10 snap: 1 / (sections.length - 1),11 end: '+=7000',12 //end: document.querySelector('main').offsetWidth,13 },14});15
16if (ScrollTrigger.isTouch > 0) {17 move;18}참고
[]isTouch] isTouch: 현재 장치의 터치 기능을 구분하는 방법로 0은 마우스/포인터만 가능(터치 없음), 1은 터치만 가능, 2는 둘 다 가능
14. 가로+세로
- html
1<main class="cont">2 <h1>가로스크롤</h1>3 <section class="item" id="sec1">4 <h2 class="num">1</h2>5 <div class="box"></div>6 </section>7 <section class="item" id="sec2">8 <h2 class="num">2</h2>9 <div class="box"></div>10 </section>11 <div class="horizontal">12 <section class="item" id="sec3">13 <h2 class="num">3</h2>14 <div class="box"></div>15 </section>16 <section class="item" id="sec4">17 <h2 class="num">4</h2>18 <div class="box"></div>19 </section>20 <section class="item" id="sec5">21 <h2 class="num">5</h2>22 <div class="box"></div>23 </section>24 </div>25 <section class="item" id="sec6">26 <h2 class="num">6</h2>27 <div class="box"></div>28 </section>29 <section class="item" id="sec7">30 <h2 class="num">7</h2>31 <div class="box"></div>32 </section>33</main>- css
1main {2 overflow: hidden;3}4.horizontal {5 display: flex;6 flex-wrap: nowrap;7 width: 500%;8}9.horizontal > section {10 width: 100%;11}- js
1const horizontal = document.querySelector('.horizontal');2const sections = gsap.utils.toArray('.horizontal > section');3
4gsap.to(sections, {5 xPercent: -100 * (sections.length - 1),6 ease: 'none',7 scrollTrigger: {8 trigger: horizontal,9 start: 'top top',10 end: () => '+=' + (horizontal.offsetWidth - innerWidth),11 pin: true,12 scrub: 1,13 snap: {14 snapTo: 1 / (sections.length - 1),15 inertia: false,16 duration: { min: 0.1, max: 0.1 },17 },18 invalidateOnRefresh: true,19 anticipatePin: 1,20 },21});참고
-
10: ’+=‘는 시작 지점으로부터의 상대적 위치. horizontal.offsetWidth는 트리거 요소의 전체 너비이며, innerWidth는 브라우저 창의 내부 너비이다. (horizontal.offsetWidth - innerWidth)는 트리거 요소의 너비에서 뷰포트의 너비를 뺀 값
-
14: snap snapTo: 스크롤 중단점. 1 / (sections.length - 1) => 1/2 을 의미하며 각 섹션 사이의 상대적인 스냅 지점을 계산한다
-
15: inertia: 스크롤 중단시 미끄러짐 적용여부. false는 스크롤을 멈추면 즉시 스냅 포인트로 이동
-
16: duration: 스냅 애니메이션의 지속 시간. min과 max는 애니메이션의 최소 및 최대 지속 시간을 초 단위로 설정. 0.1로 설정되어 있어, 스냅 애니메이션의 지속 시간이 항상 0.1초로 설정함
-
17: invalidateOnRefresh: 이 속성은 ScrollTrigger가 화면 크기 변경, 디바이스 회전 등으로 인해 뷰포트가 리프레시될 때 스냅 포인트를 재계산할지 여부를 결정. true로 설정 스냅 포인트가 자동으로 업데이트되어 항상 정확한 위치에 스냅될 수 있도록 한다
15. 가로+세로+핀+애니메이션
- html
1<main>2 <div class="first">3 <h1>Testing horizontal scrolling w/ three sections</h1>4 <h2>First Container</h2>5 </div>6 <div class="horizontal">7 <div class="description section">8 <div>9 SCROLL DOWN10 <div class="scroll-down">11 <div class="arrow"></div>12 </div>13 </div>14 </div>15 <section class="section">16 ONE17 <div class="box-1 box">box-1</div>18 </section>19 <section class="section">20 TWO21 <div class="box-2 box">box-2</div>22 </section>23 <section class="section">24 THREE25 <div class="box-3 box">box-3</div>26 </section>27 </div>28 <div class="last">Last Container</div>29</main>- css
1html {2 overflow-y: scroll;3 overflow-x: hidden;4 height: 100%;5 -webkit-overflow-scrolling: touch;6 overflow-scrolling: touch;7}8.section {9 width: 100%;10 height: 100%;11 display: flex;12 justify-content: center;13 align-items: center;14 font-weight: 600;15 font-size: 10vw;16 text-align: center;17 color: rgb(66, 8, 8);18 position: relative;19 box-sizing: border-box;20 padding: 10px;21}22.horizontal {23 width: 400%;24 height: 100vh;25 display: flex;26 flex-wrap: nowrap;27}28.first {29 display: flex;30 flex-direction: column;31 justify-content: center;32 align-items: center;33 height: 100vh;34 background: yellow;35}36.last {37 display: flex;38 height: 100vh;39 background: yellow;40}41.box {42 font-size: 5vw;43
44 text-align: center;45 line-height: 80px;46 background-color: white;47 border-radius: 8px;48 color: #222;49 font-weight: 700;50 margin-left: -200px;51 margin-top: -200px;52 will-change: transform;53 display: flex;54 z-index: 2;55}56.box.active {57 background-color: orange;58 border: 2px solid white;59}- js
1let sections = gsap.utils.toArray('.section');2
3let scrollTween = gsap.to(sections, {4 xPercent: -100 * (sections.length - 1),5 ease: 'none',6 scrollTrigger: {7 trigger: '.horizontal',8 pin: true,9 scrub: 1,10 snap: 1 / (sections.length - 1),11 end: () => '+=' + document.querySelector('.horizontal').offsetWidth,12 },13});14gsap.set('.box-1, .box-2 ', { y: 100 });15gsap.to('.box-1', {16 y: -130,17 duration: 2,18 ease: 'elastic',19 scrollTrigger: {20 trigger: '.box-1',21 containerAnimation: scrollTween,22 start: 'left center',23 toggleActions: 'play none none reset',24 id: '1',25 },26});27gsap.to('.box-2', {28 y: -120,29 rotate: 750,30 backgroundColor: '#1e90ff',31 ease: 'none',32 scrollTrigger: {33 trigger: '.box-2',34 containerAnimation: scrollTween,35 start: 'center 80%',36 end: 'center 20%',37 scrub: true,38 id: '2',39 },40});41ScrollTrigger.create({42 trigger: '.box-3',43 containerAnimation: scrollTween,44 toggleClass: 'active',45 start: 'center 60%',46 id: '3',47 markers:true,48});16. 포트폴리오-1
- 구조작성
1<div class="wrapper">2 <div class="factsContainer">3 <h2>안녕하세요</h2>4 <div class="factsContainer_sm">5 <div class="fact">6 <h3>신입 프론트엔드 김망고이다:</h3>7 <img src="https://source.unsplash.com/random" alt="" />8 <h3>일머리 좋은 신입 프론트엔드입니다</h3>9 </div>10 <div class="fact">11 <h3>좋아하는 음식</h3>12 <img src="https://source.unsplash.com/random" alt="" />13 <h3>떡순이</h3>14 </div>15 <div class="fact">16 <h3>자신있는 기술스택</h3>17 <img src="https://source.unsplash.com/random" alt="" />18 <h3>자바스크립트🤙</h3>19 </div>20 <div class="fact">21 <h3>앞으로 공부하려고 하는 기술스택</h3>22 <img src="https://source.unsplash.com/random" alt="" />23 <h3>앵귤러, 노드JS, 타입스크립트</h3>24 </div>25 <div class="fact">26 <h3>좋아하는 가수</h3>27 <img src="https://source.unsplash.com/random" alt="" />28 <h3>뉴진스 민지</h3>29 </div>30 <div class="fact">31 <h3>좋아하는 것</h3>32 <img src="https://source.unsplash.com/random" alt="" />33 <h3>강아지</h3>34 </div>35 </div>36 </div>37 <div class="socialContainer">38 <h2>저를 안뽑으시면 내일도 제생각이 나실꺼에요</h2>39 </div>40</div>- css 작성
1* {2 margin: 0;3 padding: 0;4}5
6.wrapper {7 background: #1d373f;8 overflow-x: hidden;9}10
11.factsContainer {12 min-height: 100vh;13 padding: 0em 2em;14 text-align: center;15 line-height: 10vh;16}17
18.factsContainer h2 {19 font-size: 1.8em;20 transform: scale(0);21 padding: 2em 0em;22 margin-bottom: 15vh;23 color: #f0c368;24}25
26.factsContainer_sm,27.factsContainer_sm1 {28 display: flex;29 width: 300vw;30}31
32.fact {33 display: flex;34 flex-direction: column;35 height: 40vh;36 flex: 1;37 justify-content: space-between;38 padding: 1em;39 align-items: center;40 color: #f0c368;41}42
43.fact img {44 width: 25vw;45 height: 100vh;46 margin: 1em;47}48
49.socialContainer {50 width: 100vw;51 height: 100vh;52 color: white;53 font-size: 5em;54}- js
1//애니메이션 해야할 대상이 많으므로 전체 타임라인에 부모요소를 추가한다2let scroll_tl = gsap.timeline({3 scrollTrigger: {4 trigger: '.factsContainer',5 markers: true,6 start: 'top top', //시작점 설정 윗방향기준 뷰포드 중앙에서 시작7 end: '+=300', //300px 떨어진거리에서 끝8 scrub: true,9 },10});11let facts = document.querySelectorAll('.fact');12let factW = document.querySelector('.factsContainer_sm').clientWidth;13console.log(factW);14scroll_tl.to('.factsContainer h2', {15 scale: 1.5,16 duration: 1,17 ease: 'slow',18});19scroll_tl.to(facts, {20 xPercent: -85 * (facts.length - 1), //x이동거리21 scrollTrigger: {22 trigger: '.factsContainer_sm',23 start: 'center center',24 pin: true,25 scrub: 1,26 snap: 1 / (facts.length - 1),27 // base vertical scrolling on how wide the container is so it feels more natural.28 // end: () => `+=${smallFactsContainer.offsetWidth}`29 end: () => `+=${factW}`,30 },31});32gsap.config({ trialWarn: false });:::comment_box 📢 코드설명
-
let scroll_tl=애니메이션 해야할 대상이 많으므로 전체 타임라인에 부모요소를 추가한다.
-
start = 스크롤의 시작방향과 지점 설정
-
end: 끝점 설정
:::