ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 가장 일반적인 GSAP 실수 - 학습센터 | GSAP
    Interactive/GSAP 2021. 4. 2. 15:30
    반응형

     

    from () 논리 문제 만들기

    일반적으로 .fromTo() 대신 .to() 및 .from() 트윈을 사용하는 것이 현명합니다. 왜냐하면 더 동적이기 때문입니다. 트윈이 처음으로 렌더링할 때 현재 발생하는 모든 것에서 시작 또는 종료 값을 가져옵니다. 효율적인 애니메이션을 만드는 것에 대한 기사 중 하나입니다. 그러나 이러한 동적 특성은 몇 가지 시나리오에서 현명하지 않을 수 있습니다.

     

    먼저, .from() 트윈은 제공된 값에서 현재 값으로 이동합니다. 아래 예를 살펴보세요.

    document.querySelector("button").addEventListener("click", () => 
      gsap.from(".box", {opacity: 0, duration: 3})
    );

    한 번 클릭하여 재생 해보세요. 요소가 사라지면서 작동합니다. 이제 바로 여러 번 클릭해 보세요. 애니메이션이 완료되지 않은 경우 1보다 작은 값인 불투명도를 끝점으로 사용하기 때문에 상자가 표시되지 않습니다. 이에 대한 수정은 간단합니다.

    .fromTo()를 사용하세요. 또는 미리 애니메이션을 만들고 제어 방법을 사용할 수 있습니다 (이 문서의 뒷부분에서 이 방법에 대해 자세히 설명하겠습니다.)

     

    둘째, 기본적으로 .from() 및 .fromTo() 트윈의 경우 기본적으로 immediateRender(즉시 렌더링)는 true입니다. 이는 가장 직관적인 동작이기 때문입니다 (특정 값에서 애니메이션을 적용하는 경우 바로 시작해야 함). 그러나 동일한 객체의 동일한 속성에 영향을 미치는 .to() 트윈 뒤에 .from() 트윈을 만드는 경우 어떤 일이 발생하는지 파악하세요.

    const tl = gsap.timeline()
    tl.to(".box", {x: 100});
    tl.from(".box", {x: 100});

    .box가 x 좌표를 0에서 100까지 애니메이션한 다음 다시 0으로 애니메이션할 것으로 예상할 수 있습니다. 또는 0에서 100까지 애니메이션 한 다음 100으로 유지될 것으로 예상할 수 있습니다. 어떻게 되는지 살펴 보겠습니다. .box는 x를 100에서 100으로 애니메이션한 다음 다시 0으로 애니메이션합니다. 그 이유는 무엇입니까?

    기본적으로 .to() 트윈은 재생 헤드가 실제로 움직일 때까지 렌더링을 기다립니다. (아무것도 변경되지 않았기 때문에 0의 시간에 렌더링하는 것은 CPU 주기 낭비입니다). 그러나 from()에는 instantRender : true가 있으므로 x 좌표는 현재 틱에서 즉시 100으로 점프합니다. 그런 다음 타임라인에서 첫 번째이므로 다음 틱에서 .to() 트윈을 실행하고 현재 시작 값인 100을 기록합니다. 따라서 0.5초 동안 100에서 100으로 애니메이션됩니다. 그런 다음 캐시된 값이 0 인 .from() 트윈을 종료 값으로 실행합니다.

    동일한 요소에 영향을 미치는 타임라인이 여러 개인 경우 이와 같은 상황을 포착하기가 약간 까다로울 수 있습니다. 따라서 .to() 및 .from() 트윈을 사용할 때 작동 방식에 유의하세요. 그들은 매우 강력하지만 힘에는 책임이 따릅니다. 여기에서 간단한 해결책은 .to() 트윈에서 instantRender : true를 설정하거나 .from() 트윈에서 instantRender : false를 설정하는 것입니다.

     

    세 번째 상황은 비슷하지만 repeatRefresh 및 repeat이 포함됩니다.

    일부 텍스트를 fadeIn 하고 fadeOut 하는 루프 애니메이션이 필요한 상황이 있다고 가정해 보겠습니다. 타임라인을 만들고 .from()을 사용하여 텍스트를 fadeIn한 다음 .to()를 사용하여 fadeOut 할 수 있습니다.

    const tl = gsap.timeline({repeat:-1});
    tl.set(".text", { color: "random([green, gray, orange, pink])" }, 2);	
    tl.from(chars, { opacity: 0 });
    tl.to(chars, { opacity: 0 });

    이것은 잘 작동합니다! 다음은 똑같은 것이지만 SplitText를 사용하여 조금 더 멋지게 보이게 합니다.

    const tl = gsap.timeline({repeat:-1}), 
        split = new SplitText(".quote", { type: "chars, words" }),
        chars = split.chars;
    
    tl.set(chars, { color: "random([#6fb936, #f38630, #989898, pink])" }, 2);	
    
    tl.from(chars, { 
      opacity: 0,
      duration: .1, 
      stagger: .1,
    });
    
    tl.to(chars, {
      duration: .5, 
      opacity: 0, 
      stagger: 0.05,
      ease: "power4.inOut",
    });	
    

    하지만 이것은 단지 시작의 색을 무작위로 만들 뿐입니다. 반복하는 새로운 랜덤 값을 원한다면 어떨까요? repeatRefresh: true를 추가하여 다음 작업을 확인하세요.

    애니메이션이 처음에는 올바르게 재생되지만 그 후에는 요소가 두 번 다시 fadeIn 되지 않습니다! 왜 그런 것일까요?

    repeatRefresh는 애니메이션의 끝 값을 다음 반복의 시작 값으로 사용합니다. 이 경우 텍스트 요소의 불투명도는 끝에서 모두 0입니다. 따라서 애니메이션이 두 번째로 .from()에 도달하면 트윈이 상대적이기 때문에 불투명도가 0 값에서 0 값으로 애니메이션됩니다.



    대신 우리가 원하는 것은 항상 0 값에서 1 값으로 애니메이션하는 것이므로 여기에서 가장 쉬운 수정은 .fromTo()를 사용하는 것입니다.

    const tl = gsap.timeline({repeat:-1, repeatRefresh: true}), 
        split = new SplitText(".quote", { type: "chars, words" }),
        chars = split.chars;
    
    tl.set(chars, { color: "random([#6fb936, #f38630, #989898, pink])" }, 2);	
    
    tl.fromTo(chars, { opacity: 0 }, {
      opacity: 1,
      duration: .1, 
      stagger: .1,
    });
    
    tl.to(chars, {
      duration: .5, 
      opacity: 0, 
      stagger: 0.05,
      ease: "power4.inOut",
    });	
    

    이제 우리가 원하는 대로 됩니다. .from() 앞에 .set()을 사용하는 것과 같은 다른 솔루션도 있지만, 이러한 경우 .fromTo()을 사용하는 것이 가장 쉽습니다.

     

     

    from() 또는 to()가 작동 할 때 fromTo() 사용

    가능한 경우 성능, 유지 관리, ease를 위해 .from() 또는 .to() 와 같은 상대적 트윈을 사용하는게 더 낫습니다. 따라서 필요한 경우가 아니면 .fromTo()를 사용하지 마세요. fromTo() 트윈은 나쁘지 않지만 필요할 때만 사용해야 합니다.

     

     

    GSAP를 사용하여 모든 transform을 설정하지 않습니다.

    GSAP를 사용하여 요소에 애니메이션을 적용하려는 경우 초기 변환 값(SVG 요소 포함)도 GSAP로 설정해야합니다.

    • 정확성 : 브라우저는 항상 계산된 값을 픽셀 단위로 보고하므로 % 또는 vw와 같은 다른 단위를 CSS 규칙에서 사용할 경우 GSAP가 식별할 수 없습니다. 또한 계산된 값은 matrix() 또는 matrix3d()에 있으며 회전 및 크기 조정에 대해 본질적으로 모호합니다. 0도, 360도 및 720도에 대한 matrix는 동일합니다. scaleX가 -1이면 rotation이 180도이고 scaleY가 -1인 matrix와 동일합니다. 동일한 무한한 조합이 있지만 GSAP로 transform 관련 값을 설정하면 모든 것이 완벽하게 정확한 방식으로 저장됩니다.
    • 성능 : GSAP는 transform 관련 값을 캐시하여 작업을 매우 빠르게 만듭니다. 계산된 값에서 모든 구성 요소를 구문 분석하는 데 더 많은 비용이 듭니다.

     

    스타일이 지정되지 않은 콘텐츠의 플래시가 걱정된다면 처음에는 요소를 숨긴 다음 이 콘텐츠에 적용되는 JavaScript를 통해 표시하는 작업을 사용하여 처리할 수 있습니다. 또는 CSS 규칙으로 초기 스타일을 설정하고 GSAP에서도 설정할 수 있습니다.

     

     

    xPercent 및 yPercent를 사용하지 않음

    퍼센트 기반 translation과 다른 단위를 결합 할 수 있다는 것을 알고 계셨습니까? 예를 들어 {xPercent : -50, yPercent : -50, x : 100, y : 300}과 같은 특정 오프셋으로 요소의 중심을 정렬하려는 경우 매우 유용합니다. 우리는 종종 사람들이 x 및 y 속성에 퍼센트 값을 사용하는 것을 봅니다. 이는 기술적으로 가능하지만 때때로 혼동을 일으킬 수 있습니다. 예를 들어 x와 y를 "-50%"로 설정 한 다음 나중에 xPercent : -50을 설정하면 xPercent : -100 에 있는 것처럼 움직이는 것을 볼 수 있습니다. x와 xPercent는 모두 -50 %를 갖기 때문입니다. 퍼센트 기반 translation을 설정할 때마다 일반적으로 xPercent 및 yPercent 속성을 사용하는 것이 가장 좋습니다.

    // 권장하지 않음
    x: "50%",
    y: "50%",
    
    // 권장
    xPercent: 50,
    yPercent: 50

     

     

    계속해서 애니메이션 재생성

    트윈과 타임 라인을 미리 만들면 다음과 같은 몇 가지 이점이 있습니다.

    • 성능 : 필요에 따라 올바르게 생성하는 대신 미리 작업을 수행할 수 있습니다. 또한 애니메이션 인스턴스 수가 줄어듭니다. 
    • 단순화된 논리 : 사용자 상호 작용 이벤트와 관련된 경우 특히 그렇습니다.
    • 자유 : 이벤트 발생시 애니메이션을 일시 중지하고 싶으십니까? 사용자가 뭔가를 할 때 애니메이션을 되돌리고 싶습니까? 문제 없어요. 이벤트 콜백 내에서 애니메이션을 만들 때 이러한 작업을 처리하기가 어렵습니다.

     

    미리 애니메이션을 만들 때 대부분의 경우 필요할 때까지 일시 중지 상태로 유지하는 것이 좋습니다. 그런 다음 .play(), .pause(), .reverse(), .progress(), .seek(), .restart() 및 .timeScale()과 같은 제어 메서드를 사용하여 재생 상태에 영향을 줄 수 있습니다.

     

    다음은 간단한 예입니다.

    const logotypeElements = document.querySelectorAll('.logotype');
    
    logotypeElements.forEach(container => {
      // Get the variables we're going to use
      const logo = container.querySelector('.logo'),
            squareTop = container.querySelectorAll('.right-up'),
            squareBottom = container.querySelectorAll('.left-down');
    
      // Create our animation
      const toggleLogo = gsap.timeline({
        reversed: true, 
        paused: true, 
        defaults: {duration: 0.3}
      });
      
      toggleLogo
        .to(logo, {rotation: 135}, 0)
        .to(squareTop, {fill: "#32e0c4"}, "<")
        .to(squareBottom, {fill: "#32e0c4"}, "<")
      
      // Control the animation using event listeners
      container.addEventListener("mouseenter", () => toggleLogo.play());
      container.addEventListener("mouseleave", () => toggleLogo.reverse());
    });
    

    사전에 애니메이션을 만드는 것과 관련된 자세한 내용은 효율적인 애니메이션 문서를 참조하세요.

    이 규칙의 한 가지 예외는 초기 값이 다를 수있는 경우와 같이 동적이어야하는 경우입니다. 예를 들어 차트에서 다양한 상태 사이의 막대 높이에 애니메이션을 적용하고 사용자가 다른 버튼을 빠르게 클릭 할 수 있는 경우, 매아래의 데모처럼 현재 상태(둘 사이에 있는 경우에도)에서 해당 막대가 흐르도록 매번 애니메이션을 만드는 것이 좋습니다.

    var bar1 = document.getElementById("bar1");
    var bar2 = document.getElementById("bar2");
    var bar3 = document.getElementById("bar3");
    var bar4 = document.getElementById("bar4");
    
    function drawBars(vals) {
      const offset = "-=0.6";
      const graphLa = gsap.timeline({
        defaults: {
          ease: "power2.inOut",
          duration: 0.7,
          overwrite: "auto"
        }
      })
      .to(bar1, { drawSVG: vals[0] })
      .to(bar2, { drawSVG: vals[1] }, offset)
      .to(bar3, { drawSVG: vals[2] }, offset)
      .to(bar4, { drawSVG: vals[3] }, offset)
    }
    
    
    // 상태 1 애니메이션
    document.querySelector("#graphButton1").addEventListener("click", () => drawBars(["0px 111px", "0px 126px", "0px 141px", "0px 148px"]) );
    drawBars(["0px 111px", "0px 126px", "0px 141px", "0px 148px"]); // 초기 상태를 설정합니다.
    
    // 상태 2 애니메이션
    document.querySelector("#graphButton2").addEventListener("click", () => drawBars(["0px 100px", "0px 14px", "0px 185px", "0px 219px"]) );
    
    // 상태 3 애니메이션
    document.querySelector("#graphButton3").addEventListener("click", () => drawBars(["0px 55px", "0px 74px", "0px 85px", "0px 96px"]) );
    
    // 상태 4 애니메이션
    document.querySelector("#graphButton4").addEventListener("click", () => drawBars(["0px 11px", "0px 11px", "0px 19px", "0px 19px"]) );

     

     

    완료된 타임라인에 트윈 추가

    일반적인 실수 패턴은 다음과 같습니다.

    const tl = gsap.timeline()
    tl.to(myElem, { x: 100 });
    
    myElem.addEventListener("click", () => tl.to(myElem, { x: 300 }) );

    실수를 찾았습니까? 이미 완료된 타임라인에 새 트윈을 추가하는 경우 타임라인을 다시 실행하지 않으면 호출되지 않습니다. 거의 항상 이러한 상황에서는 이전 섹션에서 다룬 지침에 따라 이전에 만든 애니메이션에 대해 제어 방법을 사용하거나 기존 타임 라인을 사용하지 않고 새 애니메이션을 만들어야합니다.

     

     

    loop를 사용하지 않음

    특정 이벤트가 각 요소에 발생할 때 여러 요소 (섹션, 카드, 버튼 등)에 동일한 효과를 적용하려면 거의 항상 loop를 사용해야합니다.

    예를 들어 하나의 버튼에만 영향을 미치려면 "button"과 같은 선택기를 사용하지 마세요.

    예를 들어, 각 버튼을 클릭 할 때 효과를 발생 시키려면 :

    // BAD: 즉시 모든 버튼에 애니메이션 효과를 줍니다! 
    gsap.effects.explode("button", { direction: "up", duration: 3 });
    
    // GOOD: 애니메이션은 각 버튼에 고유하며 클릭할 때만 해당됩니다.
    gsap.utils.toArray("button").forEach(btn => 
      btn.addEventListener("click", () => gsap.effects.explode(btn, { direction: "up", duration: 3 }))
    });

    이 루프 내에서 주어진 요소로 범위가 지정된 선택기를 사용하여 해당 요소 내부에서만 항목을 얻을 수 있습니다. 예를 들면 :

    gsap.utils.toArray(".container").forEach(container => {
      let info = container.querySelector(".information"),
          silhouette = container.querySelector(".silhouette .cover"),
          tl = gsap.timeline({ paused: true });
      
      tl.to(info, { yPercent: 0 })
        .to(silhouette, { opacity: 0 }, 0);
      
      container.addEventListener("mouseenter", () => tl.play() );
      container.addEventListener("mouseleave", () => tl.reverse() );
    });

     

     

    GSAP를 잘못 가져오기

    모듈 환경에서 GSAP를 사용할 때 사람들이 직면하는 일반적인 문제는 GSAP 또는 해당 플러그인을 잘못 가져 오는 것입니다. 대부분의 경우 설치 페이지의 관련 부분을 자세히 읽으면 가져오기 오류 오류를 방지할 수 있습니다. 이 게시물에 모든 세부 정보를 복사하지는 않겠지만, 가져오기 오류가 발생하는 경우 설치 페이지를 사용하세요. 대부분의 환경에서 사용할 올바른 가져오기 코드를 생성 할 수있는 매우 편리한 GSAP 설치 도우미 도구도 갖추고 있습니다.

     

     

    이전/장황한 구문 사용

    Lite/Max 를 삭제합니다.

    GSAP3을 로드하고 있지만 이전 구문을 사용하는 경우를 자주 봅니다. 이전 구문은 여전히 기술적으로 작동하지만 새로운 최신 GSAP3 구문은 더 매끄럽고 간단합니다. 또한 이전 구문은 GSAP4에서 지원되지 않을 것입니다.


    예를 들어 Lite/Max가 포함된 것을 사용하는 대신 GSAP을 사용하세요.

    // 이전
    TweenLite.to()
    TweenMax.from()
    new TimelineMax()
    
    // 새로운
    gsap.to()
    gsap.from()
    gsap.timeline()

     

    ease를 위해 문자열 형식 사용

    짧은 문자열 형식의 용이성은 입력이 덜 필요하므로 모듈 환경에서 추가 import 문을 피할 수 있습니다.

    // 이전
    Power2.easeOut
    Sine.easeInOut
    
    // 새로운
    "power2" // 기본값은 .out
    "sine.inOut"

     

    duration은 vars 매개 변수에 속합니다.

    var 매개 변수의 내부에 duration을 넣으려면 좀 더 많은 타이핑이 필요하지만 읽기 쉽고 직관적으로 사용할 수 있습니다. GSAP의 defaultseffects는 매우 유용하지만 duration을 두 번째 매개 변수로 사용하는 경우에는 사용할 수 없습니다.

    // 이전
    gsap.to(elem, 1, { x: 100 });
    
    // 새로운
    gsap.to(elem, { duration: 1, x: 100});
    
    // GSAP의 defaults 사용:
    const tl = gsap.timeline({ defaults: { duration: 1 } });
    tl.to(elem, { x: 100 }); // duration이 필요 없음
    tl.to(elem, { y: 100, duration: 3 }); // 기본값을 쉽게 덮어씁니다.

    GSAP3의 변경사항에 대한 자세한 내용은 GSAP3 이전 가이드를 확인하세요.

     

    숫자 값은 일반적으로 문자열일 필요가 없습니다.

    예를 들어 x transform을 100px로 설정하려면 x: "100px"라고 말할 필요가 없으며 x: 100이라고 말하면 됩니다. 간단합니다!

    단위(x: "10vw")를 변경하거나 복잡한 값(예: transformOrigin: "0px 50px")을 전달해야 하는 경우에만 숫자 값을 문자열로 전달해야 합니다.

     

    트윈의 대상은 선택기 문자열 일 수 있습니다.

    gsap.to(document.querySelectorAll(".box"), { x: 100 });
    
    gsap.to($(".box"), { x: 100 });

    위의 두 가지 모두 작동하지만 선택기 문자열을 대상으로 전달하여 단순화 할 수 있습니다. GSAP는 자동으로 .querySelectorAll()을 사용하여 일치하는 모든 요소의 목록을 가져옵니다. 따라서 위의 내용은 다음과 같이 간단하게 작성할 수 있습니다.

    gsap.to(".box", { x: 100 });

    또한 ".box, .card"와 같은 복잡한 선택기 문자열을 전달할 수 있으며 모든 .box와 .card를 선택할 수 있습니다. 또는 동일한 유형(선택자 문자열, 변수 참조, 일반 개체 등)인 요소 배열을 사용합니다.

    반응형

    댓글

Luster Sun