Animando componentes do React com GreenSock
Publicados: 2022-03-10Durante 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.
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;
- O
method
refere-se ao método GSAP com o qual você deseja interpolar. -
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 paraelement
. -
duration
é a duração da sua interpolação. É um número inteiro em segundos (sem o sufixos
!). -
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.
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.
-
gsap.fromTo()
— permite definir os valores inicial e final de uma animação. É uma combinação dos métodosfrom()
eto()
.
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.
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.
-
in()
— O movimento começa devagar, então pega o ritmo no final da animação. -
out()
— A animação começa rápido e depois desacelera no final da animação. -
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.
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.

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.

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
.

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.

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.

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
- Documentação GSAP, GreenSock
- “Guia para iniciantes da plataforma de animação GreenSock”, Nicholas Kramer, freeCodeCamp
- “Uma introdução às animações com a API de animação Greensock (GSAP)”, Zell Liew