使用 GreenSock 為 React 組件製作動畫

已發表: 2022-03-10
快速總結 ↬ GreenSock 動畫平台 (GSAP) 是一組 JavaScript 函數,可讓您隨時間對值/屬性/CSS 屬性進行補間,並將這些補間插入到時間軸中以實現更複雜的動畫。 在本文中,Blessing 解釋了 GSAP 如何通過將其功能集成到 React 組件中來構建具有各種動畫的示例登錄頁面,從而很好地與 React 庫配合使用。

在萬維網的早期,事情相當靜態和無聊。 在引入動畫之前,網頁主要基於印刷界的圖形設計和佈局。 動畫可以比靜態網頁更長時間地吸引和吸引人們的注意力,並且可以更清晰有效地傳達想法或概念。

但是,如果處理不當,動畫可能會阻礙用戶與您的產品的交互並對牽引力產生負面影響。 GreenSock 動畫平台 AKA (GSAP) 是一個功能強大的 JavaScript 庫,使前端開發人員、動畫師和設計師能夠創建基於時間軸的高性能動畫。 它允許動畫愛好者精確控制他們的動畫序列,而不是 CSS 提供的有時約束的keyframeanimation屬性。

在本文中,我將向您介紹 GSAP 的一些功能,例如scrollTriggersTimelinesEasing等,最後我們將通過使用這些功能為 React 應用程序製作動畫來構建一個直觀的用戶界面。 在代碼沙箱上查看完成的項目。

如果出現以下情況,本文將對您有用:

  • 您一直在使用 HTML、CSS 和 JavaScript 在 Web 應用程序上構建動畫。
  • 您已經在使用 animate.css、React-motion、Framer-motion 和 React-Spring 等軟件包在 React 應用程序中構建動畫網頁,此外您還想查看替代方案。
  • 您是 React 愛好者,並且希望在基於 React 的 Web 應用程序上構建複雜的動畫。

我們將研究如何從現有的 Web 項目構建各種動畫。 讓我們開始吧!

注意:本文假設您熟悉 HTML、CSS、JavaScript 和 React.js。

什麼是 GSAP?

GreenSock 動畫平台也稱為 GSAP,是一款適用於現代 Web 的超高性能、專業級動畫,允許開發人員以模塊化、聲明性和可重用的方式為他們的應用程序製作動畫。 它與框架無關,可以在任何基於 JavaScript 的項目中使用,它的包大小非常小,不會使您的應用程序膨脹。

GSAP 可以執行畫布動畫,用於創建 WebGL 體驗,創建動態 SVG 動畫並作為出色的瀏覽器支持。

跳躍後更多! 繼續往下看↓

為什麼使用 GSAP?

也許你還沒有準備好背叛其他框架,或者你還沒有被說服接受 GSAP 帶來的好處。 請允許我給您一些您可能要考慮 GSAP 的理由。

您可以構建複雜的動畫

GSAP JavaScript 庫使開發人員可以構建從簡單到非常複雜的基於物理的動畫,就像這些網站一樣,它允許開發人員和設計人員對運動進行排序並動態控制動畫。 它有很多插件,例如 DrawSVGPlugin、MorphSVGPlugin 等,這使得創建基於 SVG 的動畫和 2D/3D 動畫成為現實。 除了在 DOM 元素上集成 GSAP,您還可以在 WebGL/Canvas/ Three.js 基於上下文的動畫中使用它們。

此外,GSAP 的緩動能力非常複雜,因此與常規 CSS 動畫相比,可以創建具有多個貝塞爾曲線的高級效果。

表現

GSAP 在不同的瀏覽器中具有令人印象深刻的高性能。

根據 GSAP 團隊的說法,在他們的網站上,“GSAP 比 jQuery快 20 倍,而且 GSAP 是這個星球上最快的全功能腳本動畫工具。 在許多情況下,它甚至比 CSS3 動畫和過渡還要快。” 自己確認速度比較。

此外,GSAP 動畫在台式電腦、平板電腦和智能手機上都可以毫不費力地執行。 不需要添加一長串前綴,這一切都由 GSAP 處理。

您可以在 GSAP 上查看更多好處,或者在這裡查看 Sarah Drasner 對此有何評論。

GSAP 的缺點

你是說我應該在每個項目中都使用 GSAP 嗎? 當然不是! 我覺得,你可能不想使用 GSAP 的原因只有一個。 讓我們來了解一下!

  • GSAP 只是一個基於 JavaScript 的動畫庫,因此它需要一些 JavaScript 和 DOM 操作知識才能有效地利用其方法和 API。 這種學習曲線的缺點為剛開始使用 JavaScript 的初學者留下了更大的複雜空間。
  • GSAP 不支持基於 CSS 的動畫,因此如果您正在尋找這樣的庫,您不妨在 CSS 動畫中使用keyframes

如果您還有其他原因,請隨時在評論部分分享。

好吧,既然你的疑慮已經消除,讓我們跳到 GSAP 中的一些細節。

GSAP 基礎知識

在我們使用 React 創建動畫之前,讓我們熟悉一些 GSAP 的方法和構建塊。

如果您已經了解 GSAP 的基礎知識,則可以跳過此部分並直接跳到項目部分,我們將在滾動時使登錄頁面傾斜。

吐溫

補間是動畫中的單個動作。 在 GSAP 中,補間具有以下語法:

 TweenMax.method(element, duration, vars)

我們來看看這個語法代表什麼;

  1. method是指您想要補間的 GSAP 方法。
  2. element是您要設置動畫的元素。 如果要同時為多個元素創建補間,可以將元素數組傳遞給element
  3. duration是補間的持續時間。 它是以秒為單位的整數(沒有s後綴!)。
  4. vars是您要設置動畫的屬性的對象。 稍後再談。

GSAP 方法

GSAP 提供了許多創建動畫的方法。 在本文中,我們只提及gsap.togsap.fromgsap.fromTo等少數幾個。 您可以在他們的文檔中查看其他很酷的方法。 本節討論的方法將在本教程後面的構建項目中使用。

  • gsap.to()對象應該被動畫化的值,即動畫對象的最終屬性值 - 如下所示:
     gsap.to('.ball', {x:250, duration: 5})

為了演示to方法,下面的 codepen 演示顯示,當組件安裝時,具有250px類球的元素將在 5 秒內跨x-axis移動。 如果沒有給出持續時間,將使用默認的 500 毫秒。

請參閱 Blessing Krofegha 的 Pen [GSAP REACT DEMO1](https://codepen.io/smashingmag/pen/LYNrzMB)。

請參閱 Blessing Krofegha 的 Pen GSAP REACT DEMO1。

注意xy-axis軸分別表示水平軸和垂直軸,在 CSS 變換屬性(例如translateXtranslateY )中,它們表示為xy用於pixel-measured變換, xPercentyPercent表示基於百分比的變換。

要查看完整的代碼片段,請查看 codepen 遊樂場。

  • gsap.from() - 定義一個對象應該被動畫化的值 - 即動畫的起始值:
     gsap.from('.square', {duration:3, scale: 4})

3seconds演示展示了當組件安裝時,具有square類的元素如何在 3 秒內從 4 的比例調整大小。 檢查此 codepen 上的完整代碼片段。

請參閱 Blessing Krofegha 的 Pen [GSAP REACT DEMO2](https://codepen.io/smashingmag/pen/bGpKoPV)。

請參閱 Blessing Krofegha 的 Pen GSAP REACT DEMO2。
  • gsap.fromTo() — 讓您定義動畫的開始和結束值。 它是from()to()方法的組合。

這是它的外觀;

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

此代碼將在 3 秒內將具有類ball的元素在x-axis從不透明度 0 變為不透明度1 ,並且在3 seconds 3 secondssquare類從不透明度0變為1x-axis僅當組件安裝時x-axis 。 要查看fromTo方法的工作原理和完整的代碼片段,請查看下面 CodePen 上的演示。

請參閱 Blessing Krofegha 的 Pen [React GSAP FromTo 演示](https://codepen.io/smashingmag/pen/WNwyXex)。

請參閱 Blessing Krofegha 的 Pen React GSAP FromTo 演示。

注意:每當我們為位置屬性(例如lefttop )設置動畫時,我們必須確保相關元素必須具有relativeabsolutefixed的 CSS 位置屬性。

緩和

GSAP 官方文檔將緩動定義為更改 Tweens 時間的主要方式。 它確定對像如何在不同點改變位置。 Ease 控制 GSAP 中動畫的變化率,用於設置對象動畫的樣式。

GSAP 提供了不同類型的緩動和選項,讓您可以更好地控制動畫的行為方式。 它還提供了一個緩動可視化工具來幫助您選擇您喜歡的緩動設置。

緩動共有三種類型,它們的操作各不相同。

  1. in() — 運動開始緩慢,然後在動畫結束時加快步伐。
  2. out() — 動畫快速開始,然後在動畫結束時減速。
  3. inOut() — 動畫開始緩慢,中途加快步伐,然後緩慢結束。

請參閱 Blessing Krofegha 的 Pen [React GSAP Easing 演示](https://codepen.io/smashingmag/pen/abNKLaE)。

請參閱 Blessing Krofegha 的 Pen React GSAP Easing 演示。

在這些緩動示例中,我們鏈接了顯示三種緩動類型的補間bounce.inbounce.outbounce.inOut ,並設置動畫在開始下一個動畫之前完成的秒數的延遲,僅當該組件是安裝。 這種模式是重複的,在下一節中,我們將看到如何使用時間線更好地做到這一點。

時間線

時間軸充當多個補間的容器。 它按順序對補間進行動畫處理,並且不依賴於前一個補間的持續時間。 Timeline 可以輕鬆控制整個補間並精確管理它們的時間。

時間線可以通過創建時間線的實例來編寫,如下所示:

 gsap.timeline();

您還可以在以下代碼中以兩種不同的方式將多個補間鏈接到時間線:

 ##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', {})

讓我們用時間線重新創建前面的示例:

 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'));

useEffect鉤子中,我們創建了一個變量(tl)來保存時間線的一個實例,接下來我們使用tl變量按順序對我們的補間進行動畫處理,而不依賴於之前的補間進行動畫處理,傳遞與在前面的例子。 有關此演示的完整代碼片段,請查看下面的 codepen 遊樂場。

請參閱 Blessing Krofegha 的 Pen [React GSAP(使用時間線緩和)演示](https://codepen.io/smashingmag/pen/zYqaEmE)。

請參閱 Blessing Krofegha 的 Pen React GSAP(Easing with Timeline)演示。

現在我們已經了解了 GSAP 的一些基本構建塊,讓我們看看如何在下一節中在典型的 React 應用程序中構建完整的動畫。 讓我們開始飛行吧!

使用 React 和 GSAP 構建動畫著陸頁

讓我們為 React App 製作動畫。 確保在開始之前克隆存儲庫並運行npm install以安裝依賴項。

我們在建造什麼?

目前,我們的登錄頁麵包含一些白色背景的文本,一個不下拉菜單,實際上沒有動畫。 以下是我們將添加到登錄頁面的內容;

  • 為主頁上的文本和徽標設置動畫,以便在安裝組件時緩和下來。
  • 為菜單設置動畫,以便在單擊菜單時下拉。
  • 當頁面滾動時,使畫廊頁面中的圖像傾斜20deg
動畫頁面
動畫頁面。

查看代碼沙盒上的演示。

我們將登陸頁面的流程分解成組件,這樣很容易掌握。 這是過程;

  • 定義動畫方法,
  • 動畫文本和徽標,
  • 切換菜單,
  • 使圖像在頁面滾動時傾斜20deg

成分

  • Animate.js — 定義了所有動畫方法,
  • Image.js — 導入廚房圖像,
  • Menu.js — 包含菜單切換功能,
  • Header.js - 包含導航鏈接。

定義動畫方法

src目錄中創建一個component文件夾,並創建一個animate.js文件。 將以下代碼複製並粘貼到其中。

 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", }); };

在這裡,我們導入gsap 。 我們編寫了一個導出的箭頭函數,它可以為登錄頁面上的文本設置動畫。 請記住, gsap.from()方法定義了對象應該被動畫化的值。 該函數有一個elem參數,表示需要動畫的類。 它需要一些屬性並分配值,例如xPercent: -20 (將對象轉換 -20%),使對像不透明,使對象scale -1 ,使對像在2secease返回。

要查看這是否有效,請轉到App.js並包含以下代碼。

 ... //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> ); }

在這裡,我們從Aminate組件中導入textIntro方法。 要訪問我們過去使用的 DOM,請使用useRef Hook。 我們創建了一個變量intro ,其值設置為null 。 接下來,在useEffect掛鉤中,我們調用了textIntro方法和intro變量。 在我們的 home 組件中,在h5標記中,我們定義了ref屬性並傳入了intro變量。

動畫文本。
動畫文本。

接下來,我們有一個菜單,但單擊它時它不會下拉。 讓它發揮作用吧! 在Header.js組件中,添加以下代碼。

 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);

在這個組件中,我們定義了菜單和按鈕狀態,在useEffect掛鉤中,我們使用useHistory掛鉤監聽頁面變化,如果頁面發生變化,我們將clickedmenuName狀態值分別設置為falseMenu

為了處理我們的菜單,我們檢查了初始狀態的值是否為 false,如果為 true,我們將initialclickedmenuName的值更改為nulltrueClose 。 否則,我們檢查按鈕是否被單擊,如果為真,我們menuName更改為Menu 。 接下來,我們有一個disabledMenu函數,當它被點擊時,它會禁用我們的按鈕1sec

最後,在我們的button中,我們將disabled分配給disabled ,這是一個布爾值,當其值為true時將禁用該按鈕。 並且按鈕的onClick處理程序與toggleMenu函數相關聯。 我們在這裡所做的只是切換menu文本並將狀態傳遞給Menu組件,我們將盡快創建該組件。 讓我們在創建實際的Menu組件之前編寫使我們的菜單下拉菜單的方法。 前往Animate.js並將此代碼粘貼到其中。

 .... //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, }, }); };

在這裡,我們有一個名為menuShow的函數,它將菜單水平傾斜 2 度,使菜單緩和,使用stagger屬性偏移動畫,並在0.7secright to top變換菜單, 2degrees函數的屬性menuHide 。 要使用這些功能,請在components內創建Menu.js文件並將此代碼粘貼到其中。

 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

我們在Menu組件中所做的是導入動畫函數,它們是menuShowmenuHidetextIntro 。 接下來,我們使用refs鉤子為我們的DOM元素創建的每個引用分配變量, useRef null作為它們的值傳遞。 在useEffect掛鉤中,我們檢查menu的狀態,如果clickedfalse ,我們調用menuHide函數,否則,如果clicked狀態為 true ,我們調用menuShow函數。 最後,我們確保相關的DOM元素被傳遞了它們的特定refs ,它們是menuWrappershow1show2 。 有了這個,我們的菜單就變成了動畫。

讓我們看看它的外觀。

動畫菜單。
動畫菜單。

我們要實現的最後一個動畫是讓我們畫廊中的圖像在滾動時skew 。 現在讓我們看看我們畫廊的狀態。

沒有動畫的畫廊。
沒有動畫的畫廊。

要在我們的畫廊中實現傾斜動畫,讓我們轉到Animate.js並為其添加一些代碼。

 .... //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, }); }, }); }

我們創建了一個名為skewGallery的函數,將elem1作為參數傳遞,並註冊ScrollTrigger

ScrollTrigger是 GSAP 中的一個插件,它使我們能夠觸發基於滾動的動畫,例如在頁面滾動時傾斜圖像的情況。

為了使右邊緣粘在滾動條上,我們將right center值傳遞給transformOrigin屬性,我們還在force3D屬性設置為 true 以提高性能。

我們聲明了一個clamp變量來計算我們的偏斜並確保它不超過20degs 。 在ScrollTrigger對像中,我們將trigger屬性分配給elem1參數,這將是我們調用此函數時需要觸發的元素。 我們有一個onUpdate回調函數,裡面是一個velocity變量,用於計算當前速度並將其除以300

最後,我們通過設置其他值來根據當前值對元素進行動畫處理。 我們將skew初始設置為0並將skewY設置為0.8處的velocity變量。

接下來,我們必須在App.js文件中調用這個函數。

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

在這裡,我們從skewGalley導入了./components/Animate ,創建了一個針對圖像元素的skewImage ref。 在useEffect鉤子中,我們調用了skewGallery函數並將skewImage ref 作為參數傳遞。 最後,我們將skewImage傳遞給ref屬性。

你會同意我的看法,到目前為止,這是一段非常酷的旅程。 這是 CodeSanbox 上的預覽

本文的支持 repo 可在Github 上找到。

結論

我們已經在一個 React 項目中探索了 GSAP 的潛力,在本文中我們只觸及了皮毛,您可以使用 GSAP 做的事情沒有限制,因為它涉及動畫。 GSAP 的官方網站提供了額外的提示,以幫助您徹底了解方法和插件。 有很多演示會讓您對人們使用 GSAP 所做的事情大吃一驚。 我很想在評論部分聽聽您對 GSAP 的體驗。

資源

  1. GSAP 文檔,GreenSock
  2. “GreenSock 動畫平台初學者指南”,Nicholas Kramer,freeCodeCamp
  3. “Greensock Animation API (GSAP) 動畫簡介”,Zell Liew