Animando componentes do React com GreenSock

Publicados: 2022-03-10
Resumo rápido ↬ GreenSock Animation Platform (GSAP) é um conjunto de funções JavaScript que permite interpolar um valor/atributo/propriedade CSS ao longo do tempo e inserir essas interpolações em uma linha do tempo para animações mais complexas. Neste artigo, Blessing explica como o GSAP funciona bem com a biblioteca React, integrando suas funções em um componente React na construção de uma página de destino de exemplo com uma variedade de animações.

Durante os primeiros dias da World Wide Web, as coisas eram bastante estáticas e chatas. As páginas da Web eram principalmente baseadas em design gráfico e layouts do mundo da impressão até que as animações foram introduzidas. A animação pode envolver e prender a atenção das pessoas por mais tempo do que uma página da Web estática e comunicar uma ideia ou conceito de forma mais clara e eficaz.

No entanto, quando não feitas corretamente, as animações podem dificultar as interações do usuário com seu produto e impactar negativamente a tração. A GreenSock Animation Platform AKA (GSAP) é uma poderosa biblioteca JavaScript que permite que desenvolvedores front-end, animadores e designers criem animações baseadas em linha do tempo de alto desempenho. Ele permite que os amantes da animação assumam o controle preciso de suas sequências de animação, em vez dos quadros- keyframe e propriedades de animation às vezes restritivos que o CSS oferece.

Neste artigo, apresentarei alguns recursos do GSAP como scrollTriggers , Timelines , Easing etc, ao final vamos construir uma interface de usuário intuitiva animando um aplicativo React com esses recursos. Confira o projeto finalizado no codesandbox.

Este artigo será útil para você se:

  • Você está criando animações em aplicativos da Web com HTML, CSS e JavaScript.
  • Você já está construindo páginas animadas em aplicativos React com pacotes como animate.css, React-motion, Framer-motion e React-Spring, além de querer verificar alternativas.
  • Você é um entusiasta do React e gostaria de construir animações complexas em aplicações web baseadas em React.

Veremos como construir uma variedade de animações a partir de um projeto web existente. Vamos lá!

Observação : este artigo pressupõe que você esteja familiarizado com HTML, CSS, JavaScript e React.js.

O que é GSAP?

A GreenSock Animation Platform, também conhecida como GSAP, é uma animação de nível profissional e de alto desempenho para a web moderna que permite aos desenvolvedores animar seus aplicativos de maneira modular, declarativa e reutilizável. É agnóstico de estrutura e pode ser usado em qualquer projeto baseado em JavaScript, possui um tamanho de pacote muito mínimo e não sobrecarregará seu aplicativo.

O GSAP pode realizar animações de tela, usadas para criar experiências WebGL, e criar animações SVG dinâmicas e como excelente suporte ao navegador.

Mais depois do salto! Continue lendo abaixo ↓

Por que usar o GAP?

Talvez você ainda não esteja pronto para trair outros frameworks, ou ainda não foi convencido a abraçar as vantagens que vêm com o GSAP. Permita-me dar-lhe algumas razões pelas quais você pode querer considerar o GSAP.

Você pode construir animações complexas

A biblioteca GSAP JavaScript possibilita que os desenvolvedores criem animações baseadas em física simples a muito complexas, como no caso desses sites, permite que desenvolvedores e designers sequenciem o movimento e controlem a animação dinamicamente. Ele tem muitos plugins, como DrawSVGPlugin, MorphSVGPlugin e muito mais, o que torna a criação de animações baseadas em SVG e animações 2D/3D uma realidade. Além de integrar GSAP em elementos DOM, você pode usá-los em animações baseadas em contexto WebGL/Canvas/Three.js.

Além disso, a capacidade de easing do GSAP é bastante sofisticada, tornando possível criar efeitos avançados com vários beziers em comparação com a animação CSS regular.

atuação

O GSAP tem um alto desempenho impressionante em diferentes navegadores.

De acordo com a equipe do GSAP, em seu site, “GSAP é 20x mais rápido que jQuery, além de GSAP ser a ferramenta de animação com script completo mais rápida do planeta. É ainda mais rápido que animações e transições CSS3 em muitos casos.” Confirme a comparação de velocidade por si mesmo.

Além disso, as animações GSAP funcionam sem esforço em computadores desktop, tablets e smartphones. Não é necessário adicionar uma longa lista de prefixos, tudo isso está sendo cuidado sob o capô pelo GSAP.

Você pode conferir mais benefícios no GSAP ou ver o que Sarah Drasner tem a dizer sobre isso aqui.

Contras do GSAP

Você está dizendo que devo sempre usar o GSAP para cada projeto? Claro que não! Eu sinto que há apenas uma razão pela qual você pode não querer usar o GSAP. Vamos descobrir!

  • O GSAP é apenas uma biblioteca de animação baseada em JavaScript, portanto, requer algum conhecimento de manipulação de JavaScript e DOM para utilizar efetivamente seus métodos e APIs. Essa desvantagem da curva de aprendizado deixa ainda mais espaço para complicações para um iniciante que está começando com JavaScript.
  • O GSAP não atende a animações baseadas em CSS, portanto, se você estiver procurando por uma biblioteca para isso, também poderá usar keyframes -chave na animação CSS.

Se você tiver qualquer outro motivo, sinta-se à vontade para compartilhá-lo na seção de comentários.

Tudo bem, agora que suas dúvidas foram esclarecidas, vamos pular para alguns detalhes em GSAP.

Noções básicas de GSAP

Antes de criarmos nossa animação usando React, vamos nos familiarizar com alguns métodos e blocos de construção do GSAP.

Se você já conhece os fundamentos do GSAP, pode pular esta seção e ir direto para a seção do projeto, onde faremos uma distorção da página de destino durante a rolagem.

Interpolação

Uma interpolação é um único movimento em uma animação. No GSAP, uma interpolação tem a seguinte sintaxe:

 TweenMax.method(element, duration, vars)

Vamos dar uma olhada no que essa sintaxe representa;

  1. O method refere-se ao método GSAP com o qual você deseja interpolar.
  2. element é o elemento que você deseja animar. Se você deseja criar interpolações para vários elementos ao mesmo tempo, pode passar uma matriz de elementos para element .
  3. duration é a duração da sua interpolação. É um número inteiro em segundos (sem o sufixo s !).
  4. vars é um objeto das propriedades que você deseja animar. Mais sobre isso mais tarde.

Métodos GSAP

GSAP fornece vários métodos para criar animações. Neste artigo, mencionaremos apenas alguns, como gsap.to , gsap.from , gsap.fromTo . Você pode conferir outros métodos interessantes em sua documentação. Os métodos discutidos nesta seção serão usados ​​na construção de nosso projeto posteriormente neste tutorial.

  • gsap.to() os valores para os quais um objeto deve ser animado, ou seja, os valores da propriedade final de um objeto animado — como mostrado abaixo:
     gsap.to('.ball', {x:250, duration: 5})

Para demonstrar o método to , a demonstração do codepen abaixo mostra que um elemento com uma classe ball 250px se moverá pelo x-axis em cinco segundos quando os componentes forem montados. Se uma duração não for fornecida, um padrão de 500 milissegundos seria usado.

Veja a caneta [GSAP REACT DEMO1](https://codepen.io/smashingmag/pen/LYNrzMB) de Blessing Krofegha.

Veja a caneta GSAP REACT DEMO1 por Blessing Krofegha.

Nota : os eixos x e y-axis horizontal e vertical respectivamente, também em propriedades de transformação CSS, como translateX e translateY , são representados como x e y para transformações pixel-measured e xPercent e yPercent para transformações baseadas em porcentagem.

Para visualizar o trecho completo do código, verifique o playground do codepen.

  • gsap.from() — Define os valores dos quais um objeto deve ser animado — ou seja, os valores iniciais de uma animação:
     gsap.from('.square', {duration:3, scale: 4})

A demonstração do codepen mostra como um elemento com uma classe de square é redimensionado de uma escala de 4 em 3 3seconds quando os componentes são montados. Verifique o trecho de código completo neste codepen.

Veja a caneta [GSAP REACT DEMO2](https://codepen.io/smashingmag/pen/bGpKoPV) de Blessing Krofegha.

Veja a caneta GSAP REACT DEMO2 por Blessing Krofegha.
  • gsap.fromTo() — permite definir os valores inicial e final de uma animação. É uma combinação dos métodos from() e to() .

Veja como fica;

 gsap.fromTo('.ball',{opacity:0 }, {opacity: 1 , x: 200 , duration: 3 }); gsap.fromTo('.square', {opacity:0, x:200}, { opacity:1, x: 1 , duration: 3 });

Este código animaria o elemento com uma classe de ball de uma opacidade de 0 a uma opacidade de 1 no x-axis em 3 seconds e a classe square é animada de uma opacidade de 0 a 1 em 3 seconds no eixo x-axis somente quando o componente é montado. Para ver como o método fromTo funciona e o trecho de código completo, verifique a demonstração no CodePen abaixo.

Veja a caneta [demo React GSAP FromTo](https://codepen.io/smashingmag/pen/WNwyXex) de Blessing Krofegha.

Veja a demonstração do Pen React GSAP FromTo de Blessing Krofegha.

Nota : Sempre que estamos animando propriedades posicionais, como left e top , devemos garantir que os elementos em questão tenham uma propriedade de posição CSS relative , absolute ou fixed .

Facilitando

A documentação oficial do GSAP definiu a flexibilização como a principal maneira de alterar o tempo de suas interpolações. Ele determina como um objeto muda de posição em diferentes pontos. A facilidade controla a taxa de alteração da animação no GSAP e é usada para definir o estilo da animação de um objeto.

O GSAP oferece diferentes tipos de facilidades e opções para que você tenha mais controle sobre como sua animação deve se comportar. Ele também fornece um Visualizador de facilidade para ajudá-lo a escolher suas configurações de facilidade preferidas.

Existem três tipos de facilidades, e elas variam em suas operações.

  1. in() — O movimento começa devagar, então pega o ritmo no final da animação.
  2. out() — A animação começa rápido e depois desacelera no final da animação.
  3. inOut() — A animação começa lenta, pega o ritmo no meio e termina lentamente.

Veja a caneta [demonstração do React GSAP Easing](https://codepen.io/smashingmag/pen/abNKLaE) de Blessing Krofegha.

Veja a demonstração do Pen React GSAP Easing de Blessing Krofegha.

Neste exemplo de atenuação, encadeamos as interpolações que exibiam os três tipos de atenuação bounce.in , bounce.out e bounce.inOut , e definimos um atraso do número de segundos que a animação leva para ser concluída antes de iniciar a próxima somente quando o componente é montagens. Esse padrão é repetitivo, na próxima seção veremos como podemos usar uma linha do tempo para fazer isso melhor.

Linhas do tempo

Uma linha do tempo atua como um contêiner para várias interpolações. Ele anima interpolações em ordem sequencial e não depende da duração da interpolação anterior. A linha do tempo simplifica o controle de interpolações como um todo e o gerenciamento preciso de seu tempo.

Linhas de tempo podem ser escritas criando uma instância de uma linha de tempo assim:

 gsap.timeline();

Você também pode encadear várias interpolações a uma linha do tempo de duas maneiras diferentes, no código abaixo:

 ##Method 1 const tl = gsap.timeline(); // create an instance and assign it a variable tl.add(); // add tween to timeline tl.to('element', {}); tl.from('element', {}); ##Method 2 gsap.timeline() .add() // add tween to timeline .to('element', {}) .from('element', {})

Vamos recriar o exemplo anterior com uma linha do tempo:

 const { useRef, useEffect } = React; const Balls = () => { useEffect(() => { const tl = gsap.timeline(); tl.to('#ball1', {x:1000, ease:"bounce.in", duration: 3}) tl.to('#ball2', {x:1000, ease:"bounce.out", duration: 3, delay:3 }) tl.to('#ball3', {x:1000, ease:"bounce.inOut", duration: 3, delay:6 }) }, []); } ReactDOM.render( , document.getElementById('app')); const { useRef, useEffect } = React; const Balls = () => { useEffect(() => { const tl = gsap.timeline(); tl.to('#ball1', {x:1000, ease:"bounce.in", duration: 3}) tl.to('#ball2', {x:1000, ease:"bounce.out", duration: 3, delay:3 }) tl.to('#ball3', {x:1000, ease:"bounce.inOut", duration: 3, delay:6 }) }, []); } ReactDOM.render( , document.getElementById('app'));

Dentro de um hook useEffect , criamos uma variável (tl) que contém uma instância de uma timeline, em seguida usamos a variável tl para animar nossa interpolação em sequencial sem depender da interpolação anterior para animar, passando as mesmas propriedades como se estivesse no exemplo anterior. Para o trecho de código completo desta demonstração, verifique o playground codepen abaixo.

Veja a demonstração da caneta [React GSAP (Easing with Timeline)](https://codepen.io/smashingmag/pen/zYqaEmE) de Blessing Krofegha.

Veja a demonstração do Pen React GSAP (Easing with Timeline) de Blessing Krofegha.

Agora que temos uma ideia de alguns dos blocos básicos de construção do GSAP, vamos ver como podemos construir uma animação completa em um aplicativo React típico na próxima seção. Vamos começar o vôo!

Construindo uma landing page animada com React e GSAP

Vamos animar um aplicativo React. Certifique-se de clonar o repositório antes de começar e executar npm install para instalar as dependências.

O que estamos construindo?

Atualmente, nossa página de destino contém alguns textos com fundo branco, um menu que não é suspenso, realmente sem animação. Veja a seguir o que adicionaremos à página de destino;

  • Anime o texto e o logotipo na página inicial para facilitar quando o componente for montado.
  • Animar o menu, para que ele caia quando o menu for clicado.
  • Faça as imagens na página da galeria inclinarem 20deg quando a página rolar.
Página animada
Página animada.

Confira a demonstração em codesandbox.

Vamos dividir o processo de nossa página de destino em componentes, para que seja fácil de entender. Aqui está o processo;

  • Defina os métodos de animação,
  • Animar texto e logotipo,
  • Alternar menu,
  • Faça as imagens distorcer 20deg na rolagem da página.

componentes

  • Animate.js — Definiu todos os métodos de animação,
  • Image.js — importar imagens da galera,
  • Menu.js — Contém a funcionalidade de alternância de menu,
  • Header.js — Contém links de navegação.

Definir métodos de animação

Crie uma pasta de component dentro do diretório src e crie um arquivo animate.js . Copie e cole o seguinte código nele.

 import gsap from "gsap" import { ScrollTrigger } from "gsap/ScrollTrigger"; //Animate text export const textIntro = elem => { gsap.from(elem, { xPercent: -20, opacity: 0, stagger: 0.2, duration: 2, scale: -1, ease: "back", }); };

Aqui, importamos gsap . Escrevemos uma função de seta exportada que anima o texto na página de destino. Lembre-se de que o método gsap.from() define os valores dos quais um objeto deve ser animado. A função tem um parâmetro elem que representa a classe que precisa ser animada. Ele pega algumas propriedades e atribui valores como xPercent: -20 (transforma o objeto em -20%), não dá opacidade ao objeto, faz com que o objeto seja scale em -1 , faz o ease voltar em 2sec .

Para ver se isso funciona, App.js e inclua o código a seguir.

 ... //import textIntro import {textIntro} from "./components/Animate" ... //using useRef hook to access the textIntro DOM let intro = useRef(null) useEffect(() => { textIntro(intro) }, []) function Home() { return ( <div className='container'> <div className='wrapper'> <h5 className="intro" ref={(el) => (intro = el)}></h5> The <b>SHOPPER</b>, is a worldclass, innovative, global online ecommerce platform, that meets your everyday daily needs. </h5> </div> </div> ); }

Aqui, importamos o método textIntro do componente Aminate . Para acessar o DOM, usamos o useRef Hook. Criamos uma variável intro cujo valor é definido como null . Em seguida, dentro do hook useEffect , chamamos o método textIntro e a variável intro . Dentro do nosso componente home, na tag h5 , definimos a prop ref e passamos a variável intro .

Texto animado.
Texto animado.

Em seguida, temos um menu, mas não está caindo quando é clicado. Vamos fazer funcionar! Dentro do componente Header.js , adicione o código abaixo.

 import React, { useState, useEffect, useRef } from "react"; import { withRouter, Link, useHistory } from "react-router-dom"; import Menu from "./Menu"; const Header = () => { const history = useHistory() let logo = useRef(null); //State of our Menu const [state, setState] = useState({ initial: false, clicked: null, menuName: "Menu", }); // State of our button const [disabled, setDisabled] = useState(false); //When the component mounts useEffect(() => { textIntro(logo); //Listening for page changes. history.listen(() => { setState({ clicked: false, menuName: "Menu" }); }); }, [history]); //toggle menu const toggleMenu = () => { disableMenu(); if (state.initial === false) { setState({ initial: null, clicked: true, menuName: "Close", }); } else if (state.clicked === true) { setState({ clicked: !state.clicked, menuName: "Menu", }); } else if (state.clicked === false) { setState({ clicked: !state.clicked, menuName: "Close", }); } }; // check if out button is disabled const disableMenu = () => { setDisabled(!disabled); setTimeout(() => { setDisabled(false); }, 1200); }; return ( <header> <div className="container"> <div className="wrapper"> <div className="inner-header"> <div className="logo" ref={(el) => (logo = el)}> <Link to="/">SHOPPER.</Link> </div> <div className="menu"> <button disabled={disabled} onClick={toggleMenu}> {state.menuName} </button> </div> </div> </div> </div> <Menu state={state} /> </header> ); }; export default withRouter(Header);

Neste componente, definimos nosso menu e estado de botão, dentro do gancho useEffect , ouvimos as alterações de página usando o gancho useHistory , se a página for alterada, definimos os valores de estado clicked e menuName como false e Menu , respectivamente.

Para lidar com nosso menu, verificamos se o valor do nosso estado inicial é false, se true, alteramos o valor de initial , clicked e menuName para null , true e Close . Caso contrário, verificamos se o botão foi clicado, se verdadeiro, alteraríamos o menuName para Menu . Em seguida, temos uma função disabledMenu que desativa nosso botão por 1 segundo quando é 1sec .

Por fim, em nosso button , atribuímos disabled a disabled que é um valor booleano que desabilitará o botão quando seu valor for true . E o manipulador onClick do botão está vinculado à função toggleMenu . Tudo o que fizemos aqui foi alternar o texto do menu e passar o estado para um componente Menu , que criaríamos mais rapidamente. Vamos escrever os métodos que farão nosso menu suspenso antes de criar o componente Menu real. Vá até Animate.js e cole este código nele.

 .... //Open menu export const menuShow = (elem1, elem2) => { gsap.from([elem1, elem2], { duration: 0.7, height: 0, transformOrigin: "right top", skewY: 2, ease: "power4.inOut", stagger: { amount: 0.2, }, }); }; //Close menu export const menuHide = (elem1, elem2) => { gsap.to([elem1, elem2], { duration: 0.8, height: 0, ease: "power4.inOut", stagger: { amount: 0.07, }, }); };

Aqui, temos uma função chamada menuShow , que inclina o menu horizontalmente em 2 2degrees , facilita o menu, desloca a animação usando a propriedade stagger e transforma o menu da right to top em 0.7sec , as mesmas propriedades valem para a função menuHide . Para usar essas funções, crie o arquivo Menu.js dentro dos components e cole este código nele.

 import React, {useEffect, useRef} from 'react' import { gsap } from "gsap" import { Link } from "react-router-dom" import { menuShow, menuHide, textIntro, } from './Animate' const Menu = ({ state }) => { //create refs for our DOM elements let menuWrapper = useRef(null) let show1 = useRef(null) let show2 = useRef(null) let info = useRef(null) useEffect(() => { // If the menu is open and we click the menu button to close it. if (state.clicked === false) { // If menu is closed and we want to open it. menuHide(show2, show1); // Set menu to display none gsap.to(menuWrapper, { duration: 1, css: { display: "none" } }); } else if ( state.clicked === true || (state.clicked === true && state.initial === null) ) { // Set menu to display block gsap.to(menuWrapper, { duration: 0, css: { display: "block" } }); //Allow menu to have height of 100% gsap.to([show1, show2], { duration: 0, opacity: 1, height: "100%" }); menuShow(show1, show2); textIntro(info); } }, [state]) return ( <div ref={(el) => (menuWrapper = el)} className="hamburger-menu"> <div ref={(el) => (show1 = el)} className="menu-secondary-background-color" ></div> <div ref={(el) => (show2 = el)} className="menu-layer"> <div className="container"> <div className="wrapper"> <div className="menu-links"> <nav> <ul> <li> <Link ref={(el) => (line1 = el)} to="/about-us" > About </Link> </li> <li> <Link ref={(el) => (line2 = el)} to="/gallery" > Gallery </Link> </li> <li> <Link ref={(el) => (line3 = el)} to="/contact-us" > Contact us </Link> </li> </ul> </nav> <div ref={(el) => (info = el)} className="info"> <h3>Our Vision</h3> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit.... </p> </div> </div> </div> </div> </div> </div> ); } export default Menu

O que fizemos no componente Menu foi importar as funções animadas, que são menuShow , menuHide e textIntro . Em seguida, atribuímos variáveis ​​para cada useRef criada para nossos elementos DOM usando o gancho refs e passamos null como seus valores. Dentro do hook useEffect , verificamos o estado do menu , se clicked for false , chamamos a função menuHide , caso contrário, se o estado clicked for true chamamos a função menuShow . Por fim, garantimos que os elementos DOM em questão menuWrapper refs show1 , show2 . Com isso, temos nosso menu animado.

Vamos ver como fica.

Menu Animado.
Menu Animado.

A última animação que implementaríamos é fazer com que nossas imagens em nossa galeria fiquem skew quando rolam. Vamos ver o estado da nossa galeria agora.

Galeria sem animação.
Galeria sem animação.

Para implementar a animação skew em nossa galeria, vamos ao Animate.js e adicione alguns códigos a ele.

 .... //Skew gallery Images export const skewGallery = elem1 => { //register ScrollTrigger gsap.registerPlugin(ScrollTrigger); // make the right edge "stick" to the scroll bar. force3D: true improves performance gsap.set(elem1, { transformOrigin: "right center", force3D: true }); let clamp = gsap.utils.clamp(-20, 20) // don't let the skew go beyond 20 degrees. ScrollTrigger.create({ trigger: elem1, onUpdate: (self) => { const velocity = clamp(Math.round(self.getVelocity() / 300)); gsap.to(elem1, { skew: 0, skewY: velocity, ease: "power3", duration: 0.8, }); }, }); }

Criamos uma função chamada skewGallery , passamos elem1 como um parâmetro e registramos ScrollTrigger .

ScrollTrigger é um plugin em GSAP que nos permite acionar animações baseadas em rolagem, como neste caso de distorcer as imagens enquanto a página rola.

Para fazer a borda direita ficar na barra de rolagem, passamos o valor do right center para a propriedade transformOrigin , também definimos a propriedade force3D como true em other para melhorar o desempenho.

Declaramos uma variável clamp que calcula nossa inclinação e garante que ela não exceda 20degs . Dentro do objeto ScrollTrigger , atribuímos a propriedade trigger ao parâmetro elem1 , que seria o elemento que precisaria ser acionado ao chamarmos essa função. Temos uma função de callback onUpdate , dentro dela está uma variável de velocity que calcula a velocidade atual e a divide por 300 .

Por fim, animamos o elemento de seus valores atuais definindo outros valores. Definimos skew como inicialmente em 0 e skewY como a variável de velocity em 0.8 .

Em seguida, temos que chamar essa função em nosso arquivo App.js

 .... import { skewGallery } from "./components/Animate" function Gallery() { let skewImage = useRef(null); useEffect(() => { skewGallery(skewImage) }, []); return ( <div ref={(el) => (skewImage = el)}> <Image/> </div> ) } ....

Aqui, importamos skewGalley de ./components/Animate , criamos uma ref skewImage que tem como alvo o elemento de imagem. Dentro do hook useEffect , chamamos a função skewGallery e passamos a ref skewImage como um parâmetro. Por fim, passamos o skewImage para a ref a ser atribuída.

Você concorda comigo que foi uma jornada muito legal até agora. Aqui está a prévia no CodeSanbox

O repositório de suporte para este artigo está disponível no Github.

Conclusão

Exploramos a potência do GSAP em um projeto React, apenas arranhamos a superfície neste artigo, não há limite para o que você pode fazer com o GSAP no que diz respeito à animação. O site oficial do GSAP oferece dicas adicionais para ajudá-lo a obter uma compreensão completa dos métodos e plugins. Há muitas demos que deixariam você de queixo caído com o que as pessoas fizeram com o GSAP. Eu adoraria ouvir sua experiência com o GSAP na seção de comentários.

Recursos

  1. Documentação GSAP, GreenSock
  2. “Guia para iniciantes da plataforma de animação GreenSock”, Nicholas Kramer, freeCodeCamp
  3. “Uma introdução às animações com a API de animação Greensock (GSAP)”, Zell Liew