오늘은 GSAP(ScrollTrigger)와 Lenis 라이브러리를 써서 스크롤에 따라 반응하는 애니메이션을 만들어보았다
사용 라이브러리 (플러그인)
- GSAP
- ScrollTrigger(GSAP에서 제공하는 또다른 플러긘)
- Lenis (부드러운 스크롤 제공)
완성작
HTML 구조
<section>
<!-- 이미지 영역 -->
<div class='img_area'>
<img src='https://images.unsplash.com/photo-1607798748738-b15c40d33d57?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'>
<img src='https://images.unsplash.com/photo-1624377632657-3902bfd35958?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'>
<img src='https://images.unsplash.com/photo-1607706189992-eae578626c86?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'>
</div>
<!-- 텍스트 영역 -->
<div class='txt_area'>
<ul class='txt_container'>
<li>GSAP</li>
<li> ScrollTrigger</li>
<li>SmoothScroll</li>
</ul>
</div>
</section>
이미지 영역과 텍스트 영역을 나눠준다.
CSS
section {width: 100%; overflow-x: hidden;}
.img_area {width: 100%;}
.img_area img {width: 100%; height: 100vh; object-fit: cover; position: absolute; left: 0; top: 0;}
.txt_area {position: relative; }
.txt_area .txt_container {text-align: center; color: #fff; font-size: 3rem;}
.txt_area .txt_container li {height: 100vh; display: flex; align-items: center; justify-content: center;}
여기서 중요한 점은 img_area에 있는 이미지들을 absolute로 주어 3장 다 고정시켜주어야 한다.
텍스트는 높이를 100vh로 잡아놓은 뒤 화면 가운데에 배치하기 위해 flex,와 content, items요소들을 center로 잡는다.
txt_area에 position: relative를 준 이유는 img_area를 고정시켰기 때문.
스크립트 주소 연동
<script src='https://unpkg.com/gsap@3/dist/gsap.min.js'></script>
<script src='https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js'></script>
<script src='https://unpkg.com/lenis@1.1.14/dist/lenis.min.js'></script>
사용할 스크립트 주소를 적어 연동한다.
순서대로
GSAP -> ScrollTrigger -> Lenis
스크립트 작성
// Lenis 초기화
const lenis = new Lenis({
duration: 1.2,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
smooth: true,
});
const raf = (time) => {
lenis.raf(time);
requestAnimationFrame(raf);
};
requestAnimationFrame(raf);
일단 부드러운 스크롤 먼저 적용 시켜보자.
Lenis 초기화
- duration: 스크롤 애니메이션의 지속 시간을 설정. 여기서는 1.2초로 설정
- easing: 스크롤 애니메이션의 속도 곡선을 정의하는 함수. 여기서 사용된 함수는 타임(t) 값을 입력받아 0에서 1까지의 값을 출력하는데, 이 방식은 부드러운 시작과 끝을 만들기 위해 비선형적인 애니메이션 효과를 제공.
- smooth: 이 속성이 true로 설정되면 부드러운 스크롤 효과를 활성화.
애니메이션 프레임 요청
- raf: 이 함수는 requestAnimationFrame을 사용하여 Lenis의 스크롤 메커니즘을 매 프레임마다 업데이트함. time 매개변수는 현재 시간의 타임스탬프를 나타냄.
- lenis.raf(time): 이 호출은 Lenis의 내부 애니메이션 로직을 업데이트.
- requestAnimationFrame(raf): 이 함수는 다음 프레임에 raf 함수를 다시 호출하도록 요청. 이렇게 하여 부드러운 애니메이션 효과를 지속적으로 유지한다.
// 이미지 초기 설정
const images = document.querySelectorAll('.img_area img'); // querySelectorAll로 모든 이미지 가져와서 변수에 저장
gsap.set(images, { zIndex: 0, opacity: 0 }); // 모든 이미지의 z-index값과 opacity를 0으로 설정
gsap.set(images[0], { opacity: 1, zIndex: 1 }); // 첫 번째 이미지만 보이게 설정
// GSAP ScrollTrigger 설정
gsap.registerPlugin(ScrollTrigger);
const timeline = gsap.timeline({
scrollTrigger: {
trigger: ".img_area",
start: "top top",
end: "bottom bottom",
endTrigger: '.txt_container',
scrub: true,
pin: true,
pinSpacing: false,
onEnter: () => {
gsap.to(images[0], { opacity: 1, zIndex: 1, duration: 0.7 }); // 페이지 로드 시 첫 번째 이미지 보이기
}
},
});
// 각 텍스트 항목에 대한 ScrollTrigger 생성
gsap.utils.toArray('.txt_container li').forEach((li, index) => {
ScrollTrigger.create({
trigger: li,
start: 'top 80%',
end: 'bottom top',
onEnter: () => {
gsap.to(images[index], { opacity: 1, zIndex: 1, duration: 0.7 });
},
onLeaveBack: () => {
gsap.to(images[index], { opacity: 0, zIndex: 0, duration: 0.7 });
},
markers: true,
});
});
맨 처음 이미지 초기 설정을 해야한다.
이미지 초기 설정
- 이미지 선택: .img_area 클래스 아래의 모든 이미지 요소를 선택하여 images 변수에 저장.
- 초기 스타일 설정: 모든 이미지의 zIndex와 opacity를 0으로 설정하여 처음에는 보이지 않도록 한다.
- 첫 번째 이미지 설정: 첫 번째 이미지만 opacity를 1로 설정하여 보이도록 하고, zIndex를 1로 설정하여 다른 이미지보다 위에 표시되도록 한다.
이미지 고정하는 ScrollTrigger Pin 효과 생성
- ScrollTrigger 등록: GSAP의 ScrollTrigger 플러그인을 등록함.
- 타임라인 생성: GSAP의 타임라인을 생성하고, 스크롤과 관련된 애니메이션을 설정함.
옵션 부가 설명 보기
더보기
- trigger: 스크롤을 감지할 요소입니다. 여기서는 .img_area를 지정했습니다.
- start: 스크롤의 시작 위치를 정의합니다. 여기서는 .img_area의 최상단이 뷰포트의 최상단에 도달할 때 시작합니다.
- end: 스크롤의 끝 위치를 정의합니다. 여기서는 .img_area의 하단이 뷰포트의 하단에 도달할 때 종료합니다.
- endTrigger: 끝 위치를 결정하는 다른 요소를 지정합니다. 여기서는 .txt_container를 사용합니다.
- scrub: true로 설정하면 스크롤에 따라 애니메이션의 진행이 동기화됩니다.
- pin: true로 설정하면 해당 요소를 고정(pinning)하여 스크롤 중에도 화면에 고정됩니다.
- pinSpacing: false로 설정하면 핀 고정 시 추가 공간을 차지하지 않게 합니다.
- onEnter: 스크롤이 시작될 때 첫 번째 이미지를 보이도록 애니메이션을 설정합니다.
각 텍스트 항목에 대한 ScrollTrigger 생성
- 텍스트 항목 선택: .txt_container 내의 모든 <li> 요소를 배열로 변환하여 각각의 텍스트 항목에 대해 반복.
- ScrollTrigger 생성: 각 텍스트 항목에 대해 ScrollTrigger를 생성하여 스크롤과 관련된 애니메이션을 설정.
옵션 부가 설명 보기
더보기
- trigger: 현재 <li> 요소를 스크롤 트리거로 설정합니다.
- start: 해당 요소의 최상단이 뷰포트의 80% 지점에 도달할 때 애니메이션이 시작됩니다.
- end: 해당 요소의 하단이 뷰포트의 최상단에 도달할 때 애니메이션이 종료됩니다.
- onEnter: 해당 텍스트 항목이 뷰포트에 들어오면 해당 인덱스의 이미지를 보이도록 애니메이션을 설정합니다.
- onLeaveBack: 해당 텍스트 항목이 뷰포트를 벗어나면 해당 인덱스의 이미지를 숨기도록 애니메이션을 설정합니다.
- markers: true로 설정하면 디버깅을 위해 스크롤 트리거의 시작과 끝 지점을 화면에 표시합니다.
728x90
반응형