Post

[GSAP] ScrollTrigger: Scroll based Progress

[GSAP] ScrollTrigger: Scroll based Progress

Scroll based Progress

스크롤 기반의 진행률을 나타낼 수 있는 다양한 애니메이션을 구현합니다.

Progress Bar

CSS scale 값 0 ~ 1 을 이용해서 progress bar를 구현합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const progress = document.querySelector('.progress');
const percent = document.querySelector('.percent span');

ScrollTrigger.create({
  trigger: '.progressHolder',
  start: `top ${document.querySelector('.section01').offsetHeight - document.querySelector('.progressHolder').offsetHeight}`,
  endTrigger: '.section03',
  end: 'bottom bottom', // endTrigger인 section03의 bottom이 된다.
  pin: true,
  animation: gsap.to(progress, { scaleX: 1, ease: 'none' }),
  onUpdate({ progress }) {
    percent.textContent = Math.round(progress * 100);
  },
  scrub: true,
  id: 'pro', // ScrollTrigger.getById('pro') 을 콘솔에 찍어보면 pro ScrollTrigger 값 확인 가능
});

SVG Progress Bar

SVG를 이용한 Progress Bar를 구현할 때에는 SVG 라인의 총 길이를 알아야 합니다.
SVG 라인의 총 길이 값을 SVG의 strokeDashoffsetstrokeDasharray에 할당하고, 스크롤이 진행됨에 따라 strokeDashoffset의 값을 0으로 만들어주면 SVG라인이 그려지는 효과를 낼 수 있습니다.

SVG.getTotalLength() 함수는 SVG의 총 길이를 반환해줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const circle = document.querySelector('.circleContainer circle');
const circleLenth = circle.getTotalLength() + 1; // SVG 길이값에 1 정도 더해준다.

const rect = document.querySelector('.rectContainer rect');
const rectLength = rect.getTotalLength() + 1;

gsap.set(rect, { strokeDashoffset: rectLength, strokeDasharray: rectLength });
gsap.set(circle, { strokeDashoffset: circleLenth, strokeDasharray: circleLenth });


const progressSVG = gsap.timeline({
  defaults: { strokeDashoffset: 0, duration: 2, ease: 'none' },
})
  .to(circle, {})
  .to(rect, {}, '<');

ScrollTrigger.create({
  trigger: '.scroll-content', // document 전체에 대한 progress
  start: 'top top',
  end: 'bottom bottom',
  animation: progressSVG,
  scrub: true,
});

SVG 선의 총 길이를 구하는 함수

자주 사용되는 SVG의 총 길이를 구하고 strokeDashoffsetstrokeDasharray 값을 세팅하는 부분을 함수로 처리하면, 조금 더 효율적인 코드가 될 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function drawSVG(target) {

  // target이 string이 아닐 때 에러처리
  if ( typeof target !== 'string') throw new TypeError('drawSVG 함수에 전달된 인수는 string 타입이어야 합니다.');

  let pathLength;
  
  // target에 , 가 있을 경우 배열로 처리
  if ( target.includes(",") ) {
    const arr = target.split(",").map((a, i)=>{
      pathLength = document.querySelector(a).getTotalLength();
      gsap.set(a, {
        strokeDashoffset: pathLength,
        strokeDasharray: pathLength,
      });
      return pathLength;
    })
    
    return arr
  }

  pathLength = document.querySelector(target).getTotalLength();  // svg 선의 총 길이
  gsap.set(target, {
    strokeDashoffset: pathLength,
    strokeDasharray: pathLength,
  });
  return pathLength;
}

drawSVG(".circleContainer circle, .rectContainer rect");	// 배열로 전달

GSAP Plugin: drawSVG

GSAP의 유료 플러그인인 drawSVG을 사용하면 SVG의 총 길이 계산이나 strokeDashoffset, strokeDasharray 값 세팅 없이 간단하게 구현할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const circle = document.querySelector('.circleContainer circle');
const rect = document.querySelector('.rectContainer rect');

const progressSVG = gsap
  .timeline({ defaults: { drawSVG: 0, ease: 'none' } })		// drawSVG: 0 값만으로 구현
  .from(circle, {})
  .from(rect, {}, '<');

ScrollTrigger.create({
  trigger: '.scroll-content', // 전체에 대한 progress
  start: 'top top',
  end: 'bottom bottom',
  animation: progressSVG,
  scrub: true,
});


참고
This post is licensed under CC BY 4.0 by the author.