어떤 애니메이션 효과를 만들지 만큼 중요한 것이
언제 효과를 보여줄 것인가이다.
내 화면은 사이트 헤더 쪽에 있는데 푸터 쪽에서 애니메이션이 실행되는 사태를 막아야 한다. 그렇기 때문에 ScrollTrigger가 중요하다.
공식 사이트 혹은 CDN 혹은 npm 등 편한 방법으로 ScrollTrigger.js나 ScrollTrigger.min.js 를 다운로드한다.
스크롤을 해서 해당 요소가 보일 때 애니메이션 효과가 나타나도록 하는 것이 목적이다. 이번에는 이미지를 준비해봤다.
svg이미지를 추가하고 스크롤을 하기 위한 충분한 여백을 주었다.
<!--html-->
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="box box1"></div>
<div class="box box2"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/ScrollTrigger.min.js"></script>
<script src="script.js"></script>
</body>
<!--css-->
body { background: darkblue; }
.box1 { height: 2000px; }
.box { width: 300px; }
.box2 svg { width: 300px; height: auto; transform: rotateY(180deg)}
이제 자바스크립트로 이전 시간에 배웠던 GSAP 애니메이션을 실행하는데 이번에는 트리거를 함께 추가해야 한다.
먼저 상단에 gsap.registerPlugin(ScrollTrigger); 로 스크롤트리거 플러그인을 불러오고
gsap.to 내부에 다음 코드를 추가한다.
scrollTrigger: ".box2"
이제 해당 요소가 화면에 보이는 지점에서 애니메이션 효과가 시작된다.
하지만 여전히 아쉽다. 위 방식은 페이지 로드 후 단 한 번만 실행 가능한 일회성 애니메이션 효과이다.
스크롤을 다시 올렸다가 내려봐도 이미 실행된 애니메이션 효과가 다시 시작되지 않는다. 몇 가지 방법이 있는데 먼저 토글 액션을 알아보겠다.
이제 스크롤을 움직여보면 이미지가 재시작되거나 반대 방향으로 이동하는 걸 볼 수 있다.
toggleActions
은 onEnter
, onLeave
, onEnterBack
, onLeaveBack
네 가지로 구성된다.
스크롤과 요소의 위치에 따라서 네 가지 액션이 실행되는 것이다.
이 네 메서드의 의미는 글로 설명하는 것보다 직접 눈으로 보는 것이 낫다.
애니메이션이 실행되는 지점은 화면의 하단에 해당 요소의 상단 부분이 보일 때이다. 이 두 지점을 바꾸고 싶다면 start
메서드를 써야 한다. 하지만 그전에 인디케이터를 표시하기 위해 markers: true
를 추가한다. 그러면 scroller-start
가 화면 최하단에 있고 start가 요소의 상단에 있는 것이 확인된다.
화면의 하단 마커를 중앙으로 옮기려면 start: "top center"
를 추가한다.
gsap.registerPlugin(ScrollTrigger);
gsap.to(".box2", {
scrollTrigger: {
trigger: ".box2",
toggleActions: "resume pause reset pause",
markers: true,
start: "top center",
},
x: 400, duration: 4,
});
마찬가지로 요소의 하단에서 start 하고 싶다면 start: "bottom center"
로 바꾸면 된다.
종료 지점 역시 마찬가지다. start
대신 end
를 쓰면 된다.
이 end를 이용해서 재미있는 효과를 낼 수 있는데 그것은 잠시 후 알아보도록 하겠다.
ScrollTrigger.create({
trigger:".sc-video", //기준
start:"0% 100%", 👈
end:"100% 10%", 👈
markers:true,
👉onEnter:function(){
video.get(0).play()
videoBlur.get(0).play()
}
})
예) 맨 상단을 0%, 맨 하단을 100%로 표기
앞의 %
는 선택한 트리거 기준
을 의미하고
뒤의 %
는 윈도우 기준
을 의미한다.
즉 위의 코드는
트리거의 맨 처음(0%)이 윈도우 창의 100%, 끝에 다다랐을 때 시작!
트리거의 맨 끝(100%)이 윈도우 창의 10%에 다다랐을 때 끝남을 의미한다.
위의 트리거가 서로의 기준값에 도달했을때!! 실행되는 메소드다.
onEnter가 되었을때~ video와 videoBlur를 play해줘라!
scrub
은 간단하면서 자주 쓰이는 기능으로 스크롤이 요소 이전으로 돌아가면 애니메이션 역시 되돌아가는 기능을 말한다.
즉 애니메이션을 재사용 할 수 있는 기능으로 일회성 애니메이션 효과가 아닌 곳에 사용할 수 있다.
scrub
은 '문질러 닦다' 라는 의미인데 크게 와닿지 않고 되감기가 더 적절하지 않을까 싶다.
scrub: true
를 입력해도 되고 true 대신 수치를 입력하는 것이 더 부드럽다.
pinning
도 Scrub
만큼 자주 쓰이는 기능으로 핀으로 화면에 고정하면 기능이라고 이해하면 쉽다.
(position: sticky와 비슷)
사용법은 pin: true
, 혹은 pin: "요소의 Class/ID명"
이다.
Pinning은 원페이지 스크롤에 쓰인다.
이것을 응용하여 다음과 같은 원페이지 스크롤을 만들 수 있다.
gsap.utils
는 여러 유용한 기능을 제공하는데 그중에 toArray()
는 객체를 배열로 변환시켜 준다.
변환할 배열은 forEach
문에 있는 section이고 ScrollTrigger.create()
를 모두 적용했다.
이 부분은 배열과 forEatch
문을 공부하면 이해할 수 있지만 몰라도 쓰는데 문제 없다. pinSpacing을 적용하거나 안하는 두 경우 모두 유용하게 쓸 수 있으므로 모두 사용해보는 것을 추천한다.
ScrollTrigger
에서 pin
동작 시 레이아웃이 튀는 걸 방지하기 위한 옵션이다.
기본적으로 pin: true
→ 스크롤 도중 해당 요소(#container)를 "고정" (pinning)
하지만 pinning 시점에 갑자기 레이아웃이 재배치되면서
"툭" 하고 튀거나 버벅이는 현상이 생길 수 있음 ( 특히 복잡한 레이아웃/이미지 있을 때 )
pin
이 시작되기 "약간 전부터" 레이아웃 계산을 미리 수행해서,
pin
시작 순간에 부드럽게 넘어가게 도와주는 기능
단위는 frame 수 (보통 1 ~ 3 정도 사용)
anticipatePin: 1 → pin이 걸리기 1 frame 전에 레이아웃 준비 시작
값이 클수록 더 미리 준비하지만, 너무 크면 성능에 오히려 영향 줄 수 있음
Spinning은 스크롤시 화면이 수직이 아닌 수평으로 이동하는 기능을 말한다.
위에서 배운 pin, scrub, snap(아래에 설명) 모두 쓰이고 심지어 ease: "none" 도 없으면 안된다.
<script>
// SECTIONS에 toArray를 활용해 section들을 배열해 넣어줌!
let SECTIONS = gsap.utils.toArray("section");
gsap.to(SECTIONS, {
xPercent: -100 * ( SECTIONS.length -1 ), 1️⃣
ease: "none",
scrollTrigger: {
trigger: '#container',
end: ()=> "+=" + document.querySelector("#container").offsetWidth, 2️⃣
pin: true,
scrub: 1,
snap: 1 / (SECTIONS.length -1), 3️⃣
}
});
</script>
SECTIONS라는 배열에서 section의 갯수에서 -1 빼준다
( 1을 빼준이유는 맨 처음페이지는 제외해야해서! )
그리고 -100%를 곱해주면 토탈 -400%라는 총 이동을 해주도록 셋팅한 것!
CSS에서 섹션들이 기본 위치가 flex, flex-wrap: nowrap 으로 되어있기 때문에 가로로 쭉 나열이 되어
section별로 위치값이 아래와 같이 나열되어 있을 것이다.
section01 → 0%
section02 → +100%
section03 → +200%
section04 → +300%
section05 → +400%
그렇다면! section01은 0에서 -400%를, section02는 +100% 에서 -300%를..
이런식으로 xpercent가 이동하는 것이다.
ScrollTrigger의 end는 언제까지 스크롤 구간을 적용할지 설정하는 것으로,
document.querySelector("#container").offsetWidth
는 #container의 실제 너비를 뜻한다. (px 단위).
"+="
는 ScrollTrigger에서 "시작 위치 이후 +얼마" 라는 뜻.
스크롤이 중간에 멈췄을 때 자동으로 가장 가까운 스텝(구간)으로 "착!" 하고 정렬시키는 기능.
SECTIONS.length - 1
→ 5개 섹션이니 → 5 - 1 = 4
1 / 4 = 0.25
즉! 전체 스크롤 구간을 0% / 25% / 50% / 75% / 100%
이렇게 5등분 해서 각 섹션 위치에서 snap 적용.
✅ 결과 : 사용자가 중간에 스크롤을 멈추면 → 가장 가까운 섹션 위치로 자동 정렬.