> 웹 프론트엔드 > CSS 튜토리얼 > '메타 GSAP'로 이동 : '완벽한'무한 스크롤을위한 탐구

'메타 GSAP'로 이동 : '완벽한'무한 스크롤을위한 탐구

Joseph Gordon-Levitt
풀어 주다: 2025-03-25 10:23:10
원래의
224명이 탐색했습니다.

나는 이것이 어떻게 생겼는지 잘 모르겠다. 그러나 그것은 이야기입니다. 이 기사는 개념을 통화하는 것에 관한 것입니다. 개념은 다른 방식으로 애니메이션에 대해 생각하는 데 도움이 될 것입니다. 이 특정 예제는 무한 스크롤, 특히 카드를 복제하지 않고 카드 데크를위한 "완벽한"무한 스크롤을 특징으로합니다.

내가 왜 여기에 있습니까? 글쎄,이 모든 것이 트윗에서 시작되었습니다. 레이아웃과 사이드 스크롤링 콘텐츠에 대해 생각하게 한 트윗.

나는 그 개념을 가져다가 내 사이트에서 사용했습니다. 그리고 글을 쓰는 시점에 여전히 행동 ​​중입니다.

그런 다음 갤러리 뷰와 사이드 스크롤 개념에 대해 더 많이 생각했습니다. 우리는 라이브 스트림을 뛰어 넘고 오래된 Apple“Cover Flow”패턴과 같은 것을 만들기로 결정했습니다. 기억하세요?

이것을 만들기위한 나의 첫 번째 생각은 내가 이것을 만들겠다고 가정하므로 위의 데모에서와 같이“진보적 인 향상”을 사용하는 방식으로 JavaScript없이 작동합니다. 나는 Greensock과 Scrolltrigger를 잡고 우리는 갔다. 나는 그 일에서 꽤 실망했다. 나는 무언가가 있었지만 내가 원하는 방식으로 작동하기 위해 무한한 스크롤을 얻을 수 없었습니다. "다음"및 "이전"버튼은 공을 재생하고 싶지 않았습니다. 여기에서 볼 수 있으며 수평 스크롤이 필요합니다.

그래서 Greensock 포럼에서 새로운 스레드를 열었습니다. 내가 진지한 학습을 ​​위해 나 자신을 열려고한다는 것을 거의 알지 못했습니다! 우리는 버튼으로 문제를 해결했습니다. 그러나 나라, 나는 다른 것이 가능한지 물어봐야했다. 무한 스크롤을하는“깨끗한”방법이 있었습니까? 나는 스트림에서 무언가를 시도했지만 운이 없었습니다. 나는 궁금했다. 나는이 펜에 사용 된 기술을 스크롤 트리거 릴리스를 위해 만든 기술을 시도했습니다.

초기 답변은 다음과 같은 일이 까다 롭다는 것입니다.

스크롤의 무한한 것들에 대한 어려운 부분은 스크롤 막대가 제한되어 있고 원하는 효과는 그렇지 않다는 것입니다. 따라서 실제 스크롤 위치를 실제로 사용하는 대신이 데모 (스크롤 트리거 데모 섹션에 있음)와 같은 스크롤 위치를 루프 (스크롤 트리거 데모 섹션에서 찾을 수 있음)와 같은 스크롤 관련 내비게이션 이벤트 (예 : 휠 이벤트)에 직접 연결해야합니다.

나는 그것이 사실이라고 생각했고“as-is”를 떠나게되어 기뻤습니다. 며칠이 지났고 잭은 내가 파기 시작했을 때 내 마음을 날려 버린 답장을 떨어 뜨 렸습니다. 그리고 지금, 많은 일을 겪은 후, 나는 기술을 당신과 공유하기 위해 왔습니다.

무엇이든 애니메이션

GSAP가 종종 간과되는 한 가지는 거의 모든 것을 애니메이션 할 수 있다는 것입니다. 이것은 종종 시각적 인 것이 애니메이션에 대해 생각할 때 봄이기 때문에 무언가의 실제 운동입니다. 우리의 첫 번째 생각은 그 과정을 메타 수준으로 가져 가서 물러서서 애니메이션에 관한 것이 아닙니다.

그러나 애니메이션 작업에 대해 더 큰 규모로 생각한 다음 레이어로 분해하십시오. 예를 들어, 만화를 연주합니다. 만화는 작곡의 모음입니다. 각 구성은 장면입니다. 그런 다음 YouTube에 있든 TV 리모컨 또는 무엇이든 사용하는 리모컨으로 원격으로 구성된 작곡 컬렉션을 문지르는 힘이 있습니다. 무슨 일이 일어나고 있는지에는 거의 세 가지 수준이 있습니다.

그리고 이것은 다양한 유형의 무한 루프를 만드는 데 필요한 트릭입니다. 이것이 바로 여기의 주요 개념입니다. 우리는 타임 라인으로 타임 라인의 플레이 헤드 위치를 애니메이션합니다. 그런 다음 스크롤 위치로 해당 타임 라인을 문지르 수 있습니다.

혼란스러워하는 것처럼 걱정하지 마십시오. 우리는 그것을 분해 할 것입니다.

"메타"로 이동

예에서 시작합시다. 우리는 일부 상자를 왼쪽에서 오른쪽으로 움직이는 트윈을 만들 것입니다. 여기 있습니다.

계속 왼쪽으로 오른쪽으로가는 10 개의 상자. 그것은 Greensock에서 매우 간단합니다. 여기서 우리는 애니메이션을 계속 사용하여 반복하여 반복합니다. 그러나 우리는 각 반복을 시작할 때 간격이 있습니다. 우리는 또한 Shangger를 사용하여 운동을 우주하고 있으며, 우리가 계속하면서 중요한 역할을 할 것입니다.

 gsap.fromto ( '. box', {
  xpercent : 100
}, {
  xpercent : -200,
  스 태거 : 0.5,
  기간 : 1,
  반복 : -1,
  편의 : 'None',
})
로그인 후 복사

이제 재미있는 부분이 온다. 트윈을 일시 중지하고 변수에 할당합시다. 그런 다음 연주하는 트윈을 만들어 봅시다. 우리는 트윈의 총 시간을 트윈으로하여이를 수행 할 수 있으며,이를 통해 반복과 반복 지연을 고려하면서 트윈의 플레이 헤드 트윈을 얻거나 설정할 수 있습니다.

 const shift = gsap.fromto ( '. box', {
  xpercent : 100
}, {
  일시 중지 : 사실,
  xpercent : -200,
  스 태거 : 0.5,
  기간 : 1,
  반복 : -1,
  편의 : 'None',
})

const duration = shift.duration ()

gsap.to (shift, {
  총 시간 : 기간,
  반복 : -1,
  기간 : 기간,
  편의 : 'None',
})
로그인 후 복사

이것이 우리의 첫 번째 "메타"트윈입니다. 정확히 동일하게 보이지만 다른 수준의 제어를 추가하고 있습니다. 원래 레이어에 영향을 미치지 않고이 레이어의 물건을 변경할 수 있습니다. 예를 들어, 트윈의 용이성을 Power4.in으로 변경할 수 있습니다. 이것은 애니메이션을 완전히 바꾸지 만 기본 애니메이션에 영향을 미치지 않습니다. 우리는 폴백으로 우리 자신을 보호하고 있습니다.

뿐만 아니라 타임 라인의 특정 부분 만 반복하도록 선택할 수 있습니다. 우리는 다음과 같이 다른 사람과 함께 할 수 있습니다.

그것의 코드는 다음과 같습니다.

 gsap.fromto (shift, {
  Totaltime : 2,
}, {
  총 시간 : 기간 -1,
  반복 : -1,
  기간 : 기간,
  편안 : '없음'
})
로그인 후 복사

이것이 어디로 가는지 보십니까? 그 트윈을보십시오. 계속 반복되지만 숫자는 각각 반복마다 뒤집 힙니다. 그러나 상자는 올바른 위치에 있습니다.

"완벽한"루프 달성

우리가 원래의 예로 돌아가면 각 반복 사이에 눈에 띄는 차이가 있습니다.

여기에 트릭이 온다. 모든 것을 잠금 해제하는 부분. 완벽한 루프를 구축해야합니다.

시프트를 세 번 반복하여 시작합시다. 반복을 사용하는 것과 같습니다. 3. 반복을 제거한 방법에 주목하십시오.

 const getshift = () => gsap.fromto ( '. box', {
  xpercent : 100
}, {
  xpercent : -200,
  스 태거 : 0.5,
  기간 : 1,
  편의 : 'None',
})

const loop = gsap.timeline ()
  .add (getshift ())
  .add (getshift ())
  .add (getshift ())
로그인 후 복사

우리는 초기 트윈을 트윈을 반환하는 함수로 바꾸었고 새로운 타임 라인에 세 번 추가했습니다. 그리고 이것은 우리에게 다음을 제공합니다.

좋아요. 그러나 여전히 차이가 있습니다. 이제 우리는 트윈을 추가하고 위치시키는 위치 매개 변수를 가져올 수 있습니다. 우리는 그것이 원활하기를 원합니다. 즉, 이전의 끝이 끝나기 전에 각 트윈 세트를 삽입하는 것을 의미합니다. 그것은 비틀기와 요소의 양을 기반으로 한 가치입니다.

 Const Stagger = 0.5 // 변화하는 트윈에 사용
const boxes = gsap.utils.toarray ( '. box')
const loop = gsap.timeline ({{
  반복 : -1
})
  .add (getshift (), 0)
  .add (getShift (), Boxes.Length * 스테거)
  .add (getshift (), boxes.length * stagger * 2)
로그인 후 복사

타임 라인을 반복하여 시청하는 경우 (스 태거를 조정하면서 그것이 사물에 어떤 영향을 미치는지 알 수 있습니다)…

중간에 "매끄러운"루프가 생성되는 중간에 창이있는 것을 알 수 있습니다. 우리가 시간을 조작 한 이전의 기술을 기억하십니까? 그것이 우리가 여기서해야 할 일입니다. 루프가“원활한”시간의 창을 반복하십시오.

우리는 루프의 해당 창을 통해 총 시간을 트윈을 시도 할 수 있습니다.

 const loop = gsap.timeline ({{
  일시 중지 : 사실,
  반복 : -1,
})
.add (getshift (), 0)
.add (getShift (), Boxes.Length * 스테거)
.add (getshift (), boxes.length * stagger * 2)

gsap.fromto (루프, {
  총 시간 : 4.75,
},
{
  Totaltime : '= 5',
  기간 : 10,
  편의 : 'None',
  반복 : -1,
})
로그인 후 복사

여기에서 우리는 4.75에서 The Total Time을 트윈이라고 말하고 사이클의 길이를 추가합니다. 사이클의 길이는 5입니다. 그리고 그것은 타임 라인의 중간 창입니다. 우리는 GSAP의 nifty =를 사용하여 그렇게 할 수 있습니다.

잠시 시간을내어 거기서 일어나는 일을 소화하십시오. 이것은 머리를 감싸는 가장 까다로운 부분 일 수 있습니다. 우리는 타임 라인에서 시간의 Windows를 계산하고 있습니다. 시각화하기가 어렵지만 나는 갔다.

이것은 손이 한 번 둥글면 12 초가 걸리는 시계의 데모입니다. 반복으로 반복적으로 반복됩니다. -1은 주어진 지속 시간으로 특정 시간 창을 애니메이션하기 위해 To에서 사용합니다. 시간 창을 2와 6으로 줄이면 지속 ​​시간을 1으로 변경하면 손이 2시에서 6시에서 반복됩니다. 그러나 우리는 기본 애니메이션을 바꾸지 않았습니다.

값을 구성하여 사물에 어떤 영향을 미치는지 확인하십시오.

이 시점에서 창 위치에 대한 공식을 정리하는 것이 좋습니다. 또한 각 상자가 전환하는 데 걸리는 기간 동안 변수를 사용할 수도 있습니다.

 const 기간 = 1
const cycle_duration = boxes.length * 스 태거
const start_time = cycle_duration (지속 시간 * 0.5)
const end_time = start_time cycle_duration
로그인 후 복사

세 개의 쌓인 타임 라인을 사용하는 대신 위치를 계산할 필요가 없다는 이점을 얻을 수있는 요소를 세 번 반복 할 수 있습니다. 이것을 세 가지 쌓인 타임 라인으로 시각화하는 것은 개념을 맥주하는 깔끔한 방법이며 주요 아이디어를 이해하는 데 도움이되는 좋은 방법입니다.

처음부터 하나의 큰 타임 라인을 만들기 위해 구현을 변경합시다.

 Const Stagger = 0.5
const boxes = gsap.utils.toarray ( '. box')

const loop = gsap.timeline ({{
  일시 중지 : 사실,
  반복 : -1,
})

const shifts = [... 상자, ... 상자, ... 상자]

shifts.foreach ((box, index) => {
  loop.fromto (box, {
    xpercent : 100
  }, {
    xpercent : -200,
    기간 : 1,
    편의 : 'None',
  }, index * stagger)
})
로그인 후 복사

이것은 합치기가 더 쉽고 우리에게 동일한 창을 제공합니다. 그러나 우리는 수학에 대해 생각할 필요가 없습니다. 이제 우리는 3 개의 상자 세트를 반복하고 스 태거에 따라 각 애니메이션을 배치합니다.

우리가 스 태거를 조정하면 어떻게 보일 수 있습니까? 상자를 더 가까이 뿌릴 것입니다.

그러나 이제 총 시간이 나오기 때문에 창문이 부러졌습니다. 우리는 창문을 다시 계산해야합니다. 이제 우리가 앞서 계산 한 공식을 연결하기에 좋은시기입니다.

 const 기간 = 1
const cycle_duration = stagger * boxes.length
const start_time = cycle_duration (지속 시간 * 0.5)
const end_time = start_time cycle_duration

gsap.fromto (루프, {
  Totaltime : start_time,
},
{
  Totaltime : end_time,
  기간 : 10,
  편의 : 'None',
  반복 : -1,
})
로그인 후 복사

결정된!

시작 위치를 변경하려면 "오프셋"을 도입 할 수도 있습니다.

 Const Stagger = 0.5
const 오프셋 = 5 * 스 태거
const start_time = (cycle_duration (Stagger * 0.5)) 오프셋
로그인 후 복사

이제 우리의 창은 다른 위치에서 시작합니다.

그러나 여전히 이것은 우리에게 양쪽 끝에 어색한 스택을 제공하기 때문에 크지 않습니다. 그 효과를 없애려면 상자의 "물리적"창에 대해 생각해야합니다. 또는 그들이 장면에 들어가서 종료하는 방법에 대해 생각하십시오.

우리는 예제의 창으로 document.body를 사용하겠습니다. 상자 트윈을 출구에서 입력 및 아래로 확장하는 개별 타임 라인으로 업데이트합시다. 우리는 Yoyo를 사용하고 반복 할 수 있습니다. 1은 입력 및 종료를 달성합니다.

 shifts.foreach ((box, index) => {
  const box_tl = gsap
    .Timeline ()
    .fromto (
      상자,
      {
        xpercent : 100,
      },
      {
        xpercent : -200,
        기간 : 1,
        편의 : 'None',
      }, 0
    ))
    .fromto (
      상자,
      {
        스케일 : 0,
      },
      {
        스케일 : 1,
        반복 : 1,
        요요 : 사실,
        편의 : 'None',
        기간 : 0.5,
      },
      0
    ))
  loop.add (box_tl, index * stagger)
})
로그인 후 복사

타임 라인 기간 1을 사용하는 이유는 무엇입니까? 상황을 쉽게 따라갈 수 있습니다. 상자가 중간 점에있을 때 시간이 0.5라는 것을 알고 있습니다. 완화는 우리가 일반적으로 여기서 생각하는 효과가 없다는 점은 주목할 가치가 있습니다. 실제로, 완화는 실제로 상자가 자신을 배치하는 방식에 참여할 것입니다. 예를 들어, 상자가 바로 이동하기 전에 상자를 쉽게 입을 수 있습니다.

위의 코드는 우리에게 이것을 제공합니다.

거의. 그러나 우리 상자는 중간에서 한동안 사라집니다. 이 문제를 해결하려면 즉각적인 속성을 소개하겠습니다. CSS에서는 애니메이션 필 모드 : 없음과 같이 작용합니다. 우리는 GSAP에 상자에 설정된 스타일을 유지하거나 사전 녹음하고 싶지 않다고 말합니다.

 shifts.foreach ((box, index) => {
  const box_tl = gsap
    .Timeline ()
    .fromto (
      상자,
      {
        xpercent : 100,
      },
      {
        xpercent : -200,
        기간 : 1,
        편의 : 'None',
        즉각적인 사람 : 거짓,
      }, 0
    ))
    .fromto (
      상자,
      {
        스케일 : 0,
      },
      {
        스케일 : 1,
        반복 : 1,
        Zindex : Box.Length 1,
        요요 : 사실,
        편의 : 'None',
        기간 : 0.5,
        즉각적인 사람 : 거짓,
      },
      0
    ))
  loop.add (box_tl, index * stagger)
})
로그인 후 복사

그 작은 변화는 우리를 위해 물건을 고정시킵니다! z-index : boxes.length를 포함한 방법에 유의하십시오. 그것은 Z- 인덱스 문제로부터 우리를 보호해야합니다.

거기에 우리는 그것을 가지고 있습니다! 우리의 첫 무한한 원활한 루프. 중복 요소가없고 완벽한 연속. 우리는 시간을 굽고 있습니다! 당신이 지금까지 얻었다면 등을 두드리십시오! ?

한 번에 더 많은 상자를보고 싶다면 타이밍, 스 태거 및 편안함을 땜질 할 수 있습니다. 여기에서 우리는 0.2의 비틀 거리며 믹스에 불투명도도 도입했습니다.

여기서 핵심 부분은 불투명도 전환이 스케일보다 빠르도록 반복 텔레이를 사용할 수 있다는 것입니다. 0.25 초 이상으로 사라집니다. 0.5 초 기다립니다. 0.25 초 이상으로 사라집니다.

 .fromto (
  상자, {
    불투명도 : 0,
  }, {
    불투명도 : 1,
    기간 : 0.25,
    반복 : 1,
    RetureDelay : 0.5,
    즉각적인 사람 : 거짓,
    편의 : 'None',
    요요 : 사실,
  }, 0)
로그인 후 복사

시원한! 우리는 우리가 원하는 모든 것을 안팎으로 전환 할 수있었습니다. 여기서 가장 중요한 것은 우리에게 무한 루프를주는 시간의 창이 있다는 것입니다.

이것을 스크롤에 연결합니다

이제 우리는 원활한 루프를 갖기 때문에 스크롤에 연결합시다. 이를 위해 GSAP의 ScrollTrigger를 사용할 수 있습니다. 루핑 창을 문지르려면 여분의 트윈이 필요합니다. 우리가 어떻게 루프를 정지 시키도록 설정했는지 주목하십시오.

 const loop_head = gsap.fromto (loop, {
  Totaltime : start_time,
},
{
  Totaltime : end_time,
  기간 : 10,
  편의 : 'None',
  반복 : -1,
  일시 중지 : 사실,
})

const scrub = gsap.to (loop_head, {
  Totaltime : 0,
  일시 중지 : 사실,
  기간 : 1,
  편의 : 'None',
})
로그인 후 복사

여기서 트릭은 ScrollTrigger를 사용하여 총 스크럽의 총 시간을 업데이트하여 루프의 플레이 헤드를 문지르는 것입니다. 이 스크롤을 설정할 수있는 다양한 방법이 있습니다. 우리는 그것을 가로로 만들거나 컨테이너에 묶을 수 있습니다. 그러나 우리가 할 일은 상자를 .boxes 요소로 랩핑하고 뷰포트에 고정하는 것입니다. (이것은 뷰포트에서의 위치를 ​​수정합니다.) 우리는 또한 수직 스크롤을 고수합니다. 데모를 확인하여 .box의 스타일을 확인하여 뷰포트 크기로 물건을 설정하십시오.

 'https://cdn.skypack.dev/gsap/scrolltrigger'에서 scrolltrigger 가져 오기
gsap.registerplugin (scrolltrigger)

scrolltrigger.create ({
  시작 : 0,
  끝 : '= 2000',
  수평 : 거짓,
  PIN : '.boxes',
  onupdate : self => {
    scrub.vars.totaltime = loop_head.duration () * self.progress
    scrub.invalidate (). 다시 시작 ()
  }
})
로그인 후 복사

중요한 부분은 Onupdate 내부입니다. 그곳에서 스크롤 진행 상황을 기반으로 트윈의 총 시간을 설정했습니다. 무효화 호출은 스크럽의 내부적으로 기록 된 위치를 플러시합니다. 다시 시작하면 설정 한 새 총 시간에 위치를 설정합니다.

시도해보십시오! 타임 라인에서 앞뒤로 이동하여 위치를 업데이트 할 수 있습니다.

얼마나 멋진가요? 타임 라인의 창인 타임 라인을 문지르는 타임 라인을 스크롤하여 스크롤 할 수 있습니다. 여기서 일어나는 일이기 때문에 잠시 동안 소화하십시오.

무한 스크롤을위한 시간 여행

지금까지 우리는 시간을 조작했습니다. 이제 우리는 시간 여행에갑니다!

이를 위해 다른 GSAP 유틸리티를 사용할 것이며 더 이상 LOOP_HEAD의 총 시간을 문지르지 않을 것입니다. 대신 프록시를 통해 업데이트 할 것입니다. 이것은 "메타"GSAP의 또 다른 좋은 예입니다.

플레이 헤드 위치를 표시하는 프록시 객체부터 시작하겠습니다.

 const playhead = {위치 : 0}
로그인 후 복사

이제 스크럽을 업데이트하여 위치를 업데이트 할 수 있습니다. 동시에, 우리는 GSAP의 랩 유틸리티를 사용할 수 있으며, 이는 LOOP_HEAD 기간 주위에 위치 값을 감습니다. 예를 들어, 지속 시간이 10이고 값 11을 제공하는 경우 1이됩니다.

 const position_wrap = gsap.utils.wrap (0, loop_head.duration ())

const scrub = gsap.to (playhead, {
  위치 : 0,
  onupdate : () => {
    loop_head.totaltime (position_wrap (playhead.position))
  },
  일시 중지 : 사실,
  기간 : 1,
  편의 : 'None',
})
로그인 후 복사

마지막으로, 스크럽의 올바른 변수를 업데이트 할 수 있도록 스크롤 트리거를 수정해야합니다. 그것은 TotalTime 대신 위치입니다.

 scrolltrigger.create ({
  시작 : 0,
  끝 : '= 2000',
  수평 : 거짓,
  PIN : '.boxes',
  onupdate : self => {
    scrub.vars.position = loop_head.duration () * self.progress
    scrub.invalidate (). 다시 시작 ()
  }
})
로그인 후 복사

이 시점에서 우리는 프록시로 전환했으며 어떤 변화도 볼 수 없습니다.

스크롤 할 때 무한 루프를 원합니다. 우리의 첫 번째 생각은 스크롤 진행 상황을 완료 할 때 시작으로 스크롤하는 것입니다. 그리고 정확히 그렇게 할 것입니다. 뒤로 스크롤하십시오. 그것이 우리가하고 싶은 일이지만, 우리는 플레이 헤드가 뒤로 문지르기를 원하지 않습니다. 이것은 TotalTime이 오는 곳입니다. 기억하십니까? 반복 및 반복 지연을 포함하는 Totalduration에 따라 플레이 헤드의 위치를 ​​가져 오거나 설정합니다.

예를 들어, 루프 헤드의 지속 시간이 5이고 우리는 거기에 도착했다. 우리는 0으로 다시 문지르지 않을 것이다. 대신, 우리는 루프 헤드를 10으로 계속 문지릅니다. 계속 진행하면 15로 이동합니다. 한편, 우리는 반복 변수를 추적 할 것입니다. 또한 진행 임계 값에 도달 할 때만 반복을 업데이트 할 수 있습니다.

반복 변수부터 시작하겠습니다.

 반복 = 0을하자
로그인 후 복사

이제 ScrollTrigger 구현을 업데이트하겠습니다.

 const trigger = scrolltrigger.create ({
  시작 : 0,
  끝 : '= 2000',
  수평 : 거짓,
  PIN : '.boxes',
  onupdate : self => {
    const scroll = self.scroll ()
    if (scroll> self.end -1) {
      // 제 시간에 앞으로 나아갑니다
      랩 (1, 1)
    } else if (scroll <p>현재 반복을 위치 계산에 고려하는 방법에 주목하십시오. 이것은 스크러버로 싸여 있다는 것을 기억하십시오. 우리는 또한 우리가 두루마리의 한계에 부딪 칠 때 감지하고 있으며 그것이 우리가 포장하는 지점입니다. 이 함수는 적절한 반복 값을 설정하고 새 스크롤 위치를 설정합니다.</p><pre rel="JavaScript" data-line=""> const wrap = (IterationDelta, scrollto) => {
  반복 = 반복 델타
  trigger.scroll (scrollto)
  trigger.update ()
}
로그인 후 복사

우리는 무한 스크롤을 가지고 있습니다! 당신이 느슨하게 할 수있는 스크롤 휠이 달린 멋진 마우스 중 하나가 있다면, 가십시오! 재미 있어요!

다음은 현재 반복 및 진행 상황을 표시하는 데모입니다.

스크롤 스냅

우리는 거기에 있습니다. 그러나 이와 같은 기능을 수행 할 때 항상 "좋은"것이 있습니다. 스크롤 스냅으로 시작하겠습니다. GSAP는 다른 종속성없이 gsap.utils.snap을 사용할 수 있으므로 쉽게 사용할 수 있습니다. 그것은 우리가 포인트를 제공 할 때까지 스냅하는 것을 처리합니다. 우리는 0과 1 사이의 단계를 선언하고 예제에 10 개의 상자가 있습니다. 그것은 0.1의 스냅이 우리에게 효과가 있다는 것을 의미합니다.

 const snap = gsap.utils.snap (1 / boxes.length)
로그인 후 복사

그리고 그것은 우리의 위치 값을 스냅하는 데 사용할 수있는 함수를 반환합니다.

스크롤이 종료되면 스냅하고 싶습니다. 이를 위해 ScrollTrigger에서 이벤트 리스너를 사용할 수 있습니다. 스크롤이 끝나면 특정 위치로 스크롤 할 것입니다.

 scrolltrigger.addeventListener ( 'scrollend', () => {
  scrolltoposition (scrub.vars.position)
})
로그인 후 복사

그리고 여기에 scrolltoposition이 있습니다.

 const scrolltoposition = position => {
  const snap_pos = snap (위치)
  const progress =
    (snap_pos- loop_head.duration () * 반복) / loop_head.duration ()
  const scroll = progresstoscroll (Progress)
  trigger.scroll (스크롤)
}
로그인 후 복사

우리는 여기서 무엇을하고 있습니까?

  1. 스냅 할 시점을 계산합니다
  2. 현재 진행 상황을 계산합니다. LOOP_HEAD.DURATION ()이 1이고 2.5로 스냅했다고 가정 해 봅시다. 그것은 우리에게 0.5의 진행을 제공하여 2의 반복을 초래하고, 여기서 2.5-1 * 2 / 1 === 0.5. 우리는 항상 1과 0 사이에 있도록 진행 상황을 계산합니다.
  3. 스크롤 대상 계산. 이것은 우리 스크롤리터가 다룰 수있는 거리의 일부입니다. 이 예에서는 2000 년 거리를 설정했으며 그 중 일부를 원합니다. 우리는 그것을 계산하기 위해 새로운 함수 progresstoscroll을 만듭니다.
 const progresstoscroll = progress =>
  gsap.utils.clamp (1, trigger.end -1, gsap.utils.wrap (0, 1, progress) * trigger.end)
로그인 후 복사

이 기능은 진행 값을 가져 와서 가장 큰 두루마리 거리에 매핑합니다. 그러나 우리는 클램프를 사용하여 값이 0 또는 2000이 될 수 없는지 확인합니다. 이것은 중요합니다. 우리는 무한 루프에 우리를 넣을 수 있으므로 이러한 값에 대한 스냅을 방지하고 있습니다.

거기에 가져갈 조금 있습니다. 각 스냅에 업데이트 된 값을 표시하는이 데모를 확인하십시오.

왜 물건이 훨씬 더 쇠약합니까? 스크러빙 지속 시간과 편의가 변경되었습니다. 더 작은 지속 시간과 펀치가 편안하면 우리에게 스냅을 제공합니다.

 const scrub = gsap.to (playhead, {
  위치 : 0,
  onupdate : () => {
    loop_head.totaltime (position_wrap (playhead.position))
  },
  일시 중지 : 사실,
  기간 : 0.25,
  편의 : 'Power3',
})
로그인 후 복사

그러나 해당 데모를 사용하면 문제가 있음을 알 수 있습니다. 때때로 우리가 스냅 내부를 감싸면 플레이 헤드가 점프됩니다. 우리는 우리가 스냅 할 때 포장을해야하지만 필요할 때만이를 고려해야합니다.

 const scrolltoposition = position => {
  const snap_pos = snap (위치)
  const progress =
    (snap_pos- loop_head.duration () * 반복) / loop_head.duration ()
  const scroll = progresstoscroll (Progress)
  if (progress> = 1 || 진행 <p> 그리고 이제 우리는 스냅과 함께 무한 스크롤을 가지고 있습니다!</p><h3> 다음은?</h3><p> 우리는 견고한 무한 스크롤러의 토대를 완료했습니다. 컨트롤이나 키보드 기능과 같은 물건을 추가하기 위해이를 활용할 수 있습니다. 예를 들어, 이것은 "다음"및 "이전"버튼 및 키보드 컨트롤을 연결하는 방법 일 수 있습니다. 우리가해야 할 일은 시간을 조작하는 것입니다.</p><pre rel="JavaScript" data-line=""> const next = () => scrolltoposition (scrub.vars.position- (1 / boxes.length)))
const prev = () => scrolltoposition (scrub.vars.position (1 / boxes.length)))

// 왼쪽 및 오른쪽 화살표 Plus A 및 D.
document.addeventListener ( 'keydown', event => {
  if (event.keyCode === 37 || event.KeyCode === 65) 다음 ()
  if (event.keyCode === 39 || event.KeyCode === 68) prev ()
})

document.querySelector ( '. Next'). addEventListener ( 'Click', Next)
document.querySelector ( '. prev'). addEventListener ( 'Click', prev)
로그인 후 복사

그것은 우리에게 이와 같은 것을 줄 수 있습니다.

우리는 스크롤 복장 기능을 활용하고 필요한대로 값을 충돌시킬 수 있습니다.

그게 다야!

그거? GSAP는 요소 이상을 애니메이션 할 수 있습니다! 여기서 우리는 거의 완벽한 무한 슬라이더를 만들기 위해 시간을 굽히고 조작했습니다. 중복 요소가없고 엉망이 없으며 유연성이 우수합니다.

우리가 다룬 내용을 요약합시다.

  • 우리는 애니메이션을 애니메이션 할 수 있습니다. ?
  • 시간을 조작 할 때 타이밍을 포지셔닝 도구로 생각할 수 있습니다.
  • Scrolltrigger를 사용하여 프록시를 통해 애니메이션을 문지르는 방법.
  • GSAP의 멋진 유틸리티를 사용하여 논리를 처리하는 방법.

이제 시간을 조작 할 수 있습니다! ?

"메타"GSAP를 향한 개념은 다양한 가능성을 열어줍니다. 또 무엇을 애니메이션 할 수 있습니까? 오디오? 동영상? "Cover Flow"데모에 관해서는 여기가 어디로 갔습니까!

위 내용은 '메타 GSAP'로 이동 : '완벽한'무한 스크롤을위한 탐구의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿