본문 바로가기
Web

[Web] GSAP 스크롤하면 선을 따라가는 애니메이션

by ifhead 2022. 8. 30.
반응형

이번에는 GSAP 라이브러리에서 그래픽 소스가 선을 따라가도록 만들어주는 애니메이션 기능을 소개하겠습니다.
들어가기 전에 데모를 보고 어떤 애니메이션이 가능한지 체크해보세요.

 

SVG란?

 

SVG?


SVG는 벡터 이미지 파일입니다. 위처럼 코드 형식으로도 표현할 수 있습니다. 여러분은 따라갈 패스를 만들거나 그려질 패스를 표현할 때 SVG를 이용하게 됩니다. 그 밖의 벡터 그래픽 소스도 물론 가능합니다.

GSAP는 스크롤 양과 SVG 데이터를 연관지어줍니다. 우리는 GSAP의 몇 가지 플러그인을 이용해 다양한 효과를 구현할 수 있습니다. 이 포스팅에서는 스크롤하면 따라가는 애니메이션을 구현해 보겠습니다.

 

GSAP 플러그인 불러오기

패스팔로잉 애니메이션을 구현하기 위해서 몇 개의 플러그인을 불러와야 합니다. 헤드 태그 안에 필요한 플러그인을cdn을 이용해 불러올 것입니다. 그린삭 공식 페이지에서 최신 버전을 얻을 수 있습니다

cdn 사용방법은 매우 간단합니다. 스크립트 라인을 붙여넣기만 하면 해당 라이브러리가 불러와집니다. 첨부한 html 코드를 그대로 쓰셔도 버전이 지원 중단되지만 않는다면 데모를 실행시키는 데 문제는 없을 것입니다.

실행해 볼 데모입니다.
mikel의 자전거 작품

 

html

<html>
<head>
  <link href="style.css" rel="stylesheet" type="text/css" />
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/gsap.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/ScrollTrigger.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/MotionPathPlugin.min.js"></script>
</head>

<body>
  <div id="scrollDist"></div>
  <div id="container">
    <svg id="route" viewBox="0 50 3000 600">
      <g id="tree">
        <path class="tree" d="M 50 100 C 20 80 10 40 60 30 C 120 40 140 100 50 100 L 50 180" />
      </g>

      <use transform="translate(600,50)  scale(0.8)" href="#tree" />
      <use transform="translate(1200,25)  scale(0.8)" href="#tree" />
      <use id="tree04" transform="translate(2000,-50)  scale(1.1)" href="#tree" />
      <use id="tree05" transform="translate(2200,0)  scale(1)" href="#tree" />
      <use transform="translate(3010,100)  scale(0.8)" href="#tree" />

      <g id="guy" transform="translate(-16.908 101.924) scale(.15)">
        <path d="M345.038 160c-10.853 56.475 63.919 51.44 64.962 4.955" fill="#ebebeb" stroke="#000"
          stroke-width="13.33" />

        <g class="rad">
          <circle cx="260" cy="426" r="75" fill="none" stroke="black" stroke-width="10" />
          <circle cx="260" cy="426" r="10" fill="none" stroke="black" stroke-width="5" />

          <path d="M 250 422 L 185 400" stroke="black" stroke-width="3" />
        </g>
        <g class="rad">
          <circle cx="500" cy="426" r="75" fill="none" stroke="black" stroke-width="10" />
          <circle cx="500" cy="426" r="10" fill="none" stroke="black" stroke-width="5" />
          <path d="M 490 420 L 430 400" stroke="black" stroke-width="3" />
        </g>

        <path d="M270 425h110l67-115H310l-50 105" fill="none" stroke="#000" stroke-width="13.33"
          stroke-linecap="butt" />
        <path d="M440 290l55 123" fill="none" stroke="#000" stroke-width="13.33" stroke-linecap="butt" />
        <path
          d="M320 210c28.865-15.119 43.916-12.855 50 10 5.308 19.94 21.148 35.799 40 45 4.328 2.112 19.495 8.956 30 10 7.348 11.768 9.233 24.578-10 15-27.597-1.495-55.713-23.294-70-50l-30 60c14.819 14.311 31.427 27.676 50 40 2.199 1.891 14.145 9.426 10 30l-5 55 15 5c10.114 3.649 12.603 9.521 0 10h-40c7.21-27.894 11.5-54.439 5-80-14.994-24.682-91.176-12.316-95-50-3.835-37.791 10.071-70.107 50-100z"
          fill="#ebebeb" stroke="#000" stroke-width="13.33" />
        <path d="M440 160c-17.141-24.638-37.736-39.381-80-40-40.544-.594-51.028 20.679-20 40h100z" fill="green"
          stroke="#000" stroke-width="8" transform="matrix(.93186 0 0 .9997 19.981 .049)" />
        <path d="M370 160l5.317 40" fill="none" stroke="#000" stroke-width="13.33" />
        <path d="M270 280l60 20" fill="none" stroke="#000" stroke-width="13.33" stroke-linecap="butt" />
      </g>
      <path id="path"
        d="M -300 180 L 0 180 c 60 0 180 -20 280 0 40 10 130 11.853 200 0 135 -23 240 -5 340 20 235 50 330 -32 460 -40 110 -8 310 30 330 30 120 20 290 15 350 -30 55 -40 210 -10 300 20 50 15 180 15 260 0 120 -20 180 -30 240 20 40 30 85 25 120 25 40 -0 140 -0 180 0 "
        fill="none" stroke="none" />
      <path
        d="M -350 180 L 0 180 c 60 0 180 -20 280 0 40 10 130 11.853 200 0 135 -23 240 -5 340 20 235 50 330 -32 460 -40 110 -8 310 30 330 30 120 20 290 15 350 -30 55 -40 210 -10 300 20 50 15 180 15 260 0 120 -20 180 -30 240 20 40 30 85 25 120 25 40 -0 140 -0 180 0 L 3000 600 L -350 600"
        fill="green" stroke="none" />

      <path d="M 3000 225 L 4000 225 L 4000 600 L 3000 600" fill="green" stroke="none" />
    </svg>

    <script src="script.js"></script>
  </div>
</body>
</html>

 

CSS

데모를 분석하기 전에 작품을 다 불러와봅시다. 위의 html 코드를 저장하셨다면 style.css 파일도 만들어서 한 폴더에 저장하세요.

body {
    margin: 0;
    background-color: #72c1e0;
}

#container {
    visibility: hidden;
    margin-top: 30vh;
}

#route {
    position: absolute;
    width: 500vw;
    overflow: visible;
}

.tree {
    fill: green;
    stroke: #1c5a1c;
    stroke-width: 6;
}

 

JavaScript

script.js 파일을 생성해줍니다.여기서 GSAP가 동작할 것입니다. 패스팔로잉 애니메이션 내용이 모두 담겨 있습니다.

console.clear();

// set initial states


gsap.set("#guy", {
    scaleX: 0.15
});

var wh = window.innerHeight,
    speed = 20,
    scrollDist = wh * speed,
    scrollEnd = wh * (speed - 1),
    route = document.getElementById('route'),
    routeWidth = route.getBoundingClientRect().width;
console.log(routeWidth);

gsap.set('#scrollDist', {
    width: '100%',
    height: scrollDist
})
gsap.set('#container', {
    position: 'fixed',
    width: routeWidth,
    left: 200,
    top: 0,
    autoAlpha: 1
})


//tween the svg path + cyclist + wheels 
gsap.timeline({
        defaults: {
            duration: 1,
            ease: 'none'
        },
        scrollTrigger: {
            trigger: '#scrollDist',
            start: 'top top',
            end: '+=' + scrollEnd,
            scrub: 0.3,
            onUpdate: (self) => {
                gsap.set('#container', {
                    x: -routeWidth * (self.progress)
                });

                gsap.set('.rad', {
                    rotation: () => self.direction === 1 ? 360 * 30 * (self.progress) : -360 * 30 * (self.progress),
                    transformOrigin: 'center',
                    overwrite: 'auto'
                })

                gsap.to("#guy", {
                    scaleX: () => self.direction === 1 ? 0.15 : -0.15,
                    overwrite: 'auto',
                    duration: 0.2
                });

                console.log(self.progress) //  info for position
            },

        }
    })
    .to('#guy', {
        motionPath: {
            path: "#path",
            align: "#path",
            alignOrigin: [0.5, 1],
            autoRotate: true,
            start: 0.1
        }
    })
    .to('#tree04', {
        rotation: 20,
        transformOrigin: 'center bottom',
        duration: 0.01,
        repeat: 1,
        yoyo: true
    }, 0.665)
    .to('#tree05', {
        rotation: -20,
        transformOrigin: 'center bottom',
        duration: 0.01,
        repeat: 1,
        yoyo: true
    }, 0.665)

window.onresize = () => {
    gsap.set('#container', {
        left: 200
    });
    routeWidth = route.getBoundingClientRect().width;
    ScrollTrigger.refresh()
}

 

VSCode Live Server

프리뷰를 실행해서 결과를 확인해봅시다. 라이브 서버를 이용해 cdn에 접속하여 웹페이지를 로드합니다.

이런 모습이 보일 것입니다


라이브 서버 사용방법

 

[Tips] Live Server 비주얼 스튜디오 코드 라이브 서버 설정

비주얼 스튜디오 코드 라이브 서버 설정하는 과정을 순서대로 알려드리겠습니다. 라이브 서버란? 이 확장을 VSCode에 설치하면 웹사이트를 개발하면서 브라우저에서 바로 프리뷰를 확인할 수 있

ifhead.tistory.com

 

 

스크립트 살펴보기

 

self.progress

 

콘솔 청소

console.clear()는 가능한 경우, 콘솔에 기록된 메시지를 모두 지웁니다.

애니메이팅

gsap.set

이동 시간 없이 즉시 이동


gsap.to

가장 일반적인 유형의 애니메이션은 목적지 값을 정의할 수 있도록 하는 to() 트윈입니다

 

애니메이션 경로 관리

var wh = window.innerHeight,
    speed = 20,
    scrollDist = wh * speed,
    scrollEnd = wh * (speed - 1),
    route = document.getElementById('route'),
    routeWidth = route.getBoundingClientRect().width;
console.log(routeWidth);

window.onresize = () => {
    gsap.set('#container', {
        left: 200
    });
    routeWidth = route.getBoundingClientRect().width;
    ScrollTrigger.refresh()
}

 

화면 사이즈에 따라 전체 경로의 길이가 바뀌는데, 그냥 놔두면 기대한 대로 동작하지 않을 것입니다. 애니메이션이 망가지는 것입니다. 그래서 페이지가 리사이징 될 때마다 애니메이션의 지표 값들을 바꾸어주고 있습니다.

 

 

반응형

댓글