GreenSockを使用したReactコンポーネントのアニメーション
公開: 2022-03-10ワールドワイドウェブの初期の頃、物事はかなり静的で退屈でした。 ウェブページは主に、印刷の世界からアニメーションが導入されるまでのグラフィックデザインとレイアウトに基づいていました。 アニメーションは、静的なWebページよりも長く人々の注意を引き付け、保持することができ、アイデアや概念をより明確かつ効果的に伝達します。
ただし、正しく行われないと、アニメーションがユーザーの製品との対話を妨げ、トラクションに悪影響を与える可能性があります。 GreenSock Animation Platform AKA(GSAP)は、フロントエンドの開発者、アニメーター、デザイナーがパフォーマンスの高いタイムラインベースのアニメーションを作成できるようにする強力なJavaScriptライブラリです。 これにより、アニメーション愛好家は、CSSが提供するkeyframeやanimationのプロパティを制限することなく、アニメーションシーケンスを正確に制御できます。
この記事では、 scrollTriggers 、 Timelines 、 EasingなどのGSAPのいくつかの機能を紹介し、最後に、この機能を使用してReactアプリをアニメーション化することで直感的なユーザーインターフェイスを構築します。 codesandboxで完成したプロジェクトをチェックしてください。
この記事は、次の場合に役立ちます。
- HTML、CSS、およびJavaScriptを使用してWebアプリケーションでアニメーションを作成しています。
- すでにanimate.css、React-motion、Framer-motion、React-Springなどのパッケージを使用してReactアプリでアニメーション化されたウェブページを構築しており、さらに代替案を確認したいと考えています。
- あなたはReactの愛好家であり、ReactベースのWebアプリケーションで複雑なアニメーションを作成したいと考えています。
既存のWebプロジェクトからさまざまなアニメーションを作成する方法を見ていきます。 それを手に入れよう!
注:この記事は、HTML、CSS、JavaScript、およびReact.jsに慣れていることを前提としています。
GSAPとは何ですか?
GreenSock Animation Platformは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のチームによると、彼らのWebサイトでは、「GSAPはjQueryよりも20倍高速であり、さらにGSAPは地球上で最速のフル機能のスクリプトアニメーションツールです。 多くの場合、CSS3アニメーションやトランジションよりもさらに高速です。」 速度比較を自分で確認してください。
さらに、GSAPアニメーションは、デスクトップコンピューター、タブレット、スマートフォンの両方で簡単に実行できます。 プレフィックスの長いリストを追加する必要はありません。これはすべて、GSAPによって内部的に処理されています。
GSAPのその他のメリットを確認するか、SarahDrasnerがそれについて何を言っているかをここで確認できます。
GSAPの短所
すべてのプロジェクトで常にGSAPを使用する必要があると言っていますか? もちろん違います! GSAPを使用したくない理由は1つだけだと思います。 確認してみましょう!
- GSAPはJavaScriptベースのアニメーションライブラリにすぎないため、メソッドとAPIを効果的に利用するには、JavaScriptとDOMの操作に関するある程度の知識が必要です。 この学習曲線の欠点は、JavaScriptを使い始めた初心者にとって、さらに複雑になる余地を残します。
- GSAPはCSSベースのアニメーションに対応していないため、そのようなライブラリを探している場合は、CSSアニメーションで
keyframesを使用することをお勧めします。
他に理由がある場合は、コメントセクションでお気軽に共有してください。
さて、あなたの疑問が解消されたので、GSAPのいくつかの核心に飛びつきましょう。
GSAPの基本
Reactを使用してアニメーションを作成する前に、GSAPのいくつかのメソッドとビルディングブロックについて理解しましょう。
GSAPの基本をすでに知っている場合は、このセクションをスキップしてプロジェクトセクションに直接ジャンプできます。ここで、スクロール中にランディングページを歪めます。
トゥイーン
トゥイーンは、アニメーションの1つの動きです。 GSAPでは、トゥイーンの構文は次のとおりです。
TweenMax.method(element, duration, vars)この構文が何を表すかを見てみましょう。
-
methodとは、トゥイーンしたいGSAPメソッドを指します。 -
elementは、アニメーション化する要素です。 複数の要素のトゥイーンを同時に作成する場合は、要素の配列をelementに渡すことができます。 -
durationは、トゥイーンの期間です。 これは秒単位の整数です(sサフィックスなし!)。 -
varsは、アニメートするプロパティのオブジェクトです。 これについては後で詳しく説明します。
GSAPメソッド
GSAPは、アニメーションを作成するための多数のメソッドを提供します。 この記事では、 gsap.to 、 gsap.from 、 gsap.fromToなどのいくつかについてのみ言及します。 あなたは彼らのドキュメントで他のクールな方法をチェックすることができます。 このセクションで説明する方法は、このチュートリアルの後半でプロジェクトを構築する際に使用されます。
-
gsap.to()オブジェクトをアニメーション化する必要のある値、つまりアニメーション化されたオブジェクトの終了プロパティ値—以下に示すように:gsap.to('.ball', {x:250, duration: 5})
toメソッドを示すために、以下のcodepenデモは、ボール250pxのクラスを持つ要素が、コンポーネントがマウントされるときに5秒でx-axisを横切って移動することを示しています。 期間が指定されていない場合、デフォルトの500ミリ秒が使用されます。
BlessingKrofeghaによるペン[GSAPREACT DEMO1](https://codepen.io/smashingmag/pen/LYNrzMB)を参照してください。
注: x軸とy-axis軸はそれぞれ水平軸と垂直軸を表し、 translateXやtranslateYなどのCSS変換プロパティでも、 pixel-measuredはxとyとして、パーセンテージベースの変換の場合はxPercentとyPercentとして表されます。
コードの完全なスニペットを表示するには、codepenプレイグラウンドを確認してください。
-
gsap.from()—オブジェクトをアニメーション化する値を定義します—つまり、アニメーションの開始値:gsap.from('.square', {duration:3, scale: 4})
3secondsデモは、コンポーネントがマウントされたときに、 squareのクラスを持つ要素が3秒で4のスケールからどのようにサイズ変更されるかを示しています。 このcodepenで完全なコードスニペットを確認してください。
BlessingKrofeghaによるペン[GSAPREACT DEMO2](https://codepen.io/smashingmag/pen/bGpKoPV)を参照してください。
-
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 }); このコードは、不透明度0からx-axis全体の不透明度1まで3 secondsでballのクラスを使用して要素をアニメーション化し、 squareのクラスはx-axis軸全体で不透明度0から1まで3 secondsでアニメーション化されます。コンポーネントがマウントされるときのみx-axis 。 fromToメソッドがどのように機能するかと完全なコードスニペットを確認するには、以下のCodePenのデモを確認してください。
BlessingKrofeghaによるペン[ReactGSAP FromToデモ](https://codepen.io/smashingmag/pen/WNwyXex)を参照してください。
注: leftやtopなどの位置プロパティをアニメーション化するときは常に、関連する要素がrelative 、 absolute 、またはfixedのいずれかのCSS位置プロパティを持っていることを確認する必要があります。
緩和
GSAPの公式ドキュメントでは、Tweenのタイミングを変更する主な方法としてイージングを定義しています。 オブジェクトがさまざまなポイントでどのように位置を変更するかを決定します。 Easeは、GSAPのアニメーションの変化率を制御し、オブジェクトのアニメーションのスタイルを設定するために使用されます。
GSAPには、アニメーションの動作をより細かく制御できるように、さまざまな種類の使いやすさとオプションが用意されています。 また、好みのイーズ設定を選択するのに役立つイーズビジュアライザーも提供します。
イーズには3つのタイプがあり、操作が異なります。
-
in()—モーションはゆっくりと開始し、アニメーションの終わりに向かってペースを上げます。 -
out()—アニメーションは速く始まり、アニメーションの終わりに遅くなります。 -
inOut()—アニメーションはゆっくりと始まり、途中でペースを上げ、ゆっくりと終わります。
BlessingKrofeghaによるペン[ReactGSAP Easingデモ](https://codepen.io/smashingmag/pen/abNKLaE)を参照してください。
これらのイージングの例では、3種類のbounce.in 、 bounce.out 、 bounce.inOutを表示するトゥイーンをチェーンし、次のアニメーションを開始する前にアニメーションが完了するまでにかかる秒数の遅延を設定しました。コンポーネントはマウントです。 このパターンは繰り返されます。次のセクションでは、タイムラインを使用してこれをより適切に行う方法を説明します。
タイムライン
タイムラインは、複数のトゥイーンのコンテナとして機能します。 トゥイーンを順番にアニメートし、前のトゥイーンの継続時間には依存しません。 タイムラインを使用すると、トゥイーン全体を簡単に制御し、タイミングを正確に管理できます。
タイムラインは、次のようにタイムラインのインスタンスを作成することで記述できます。
gsap.timeline();次のコードでは、2つの異なる方法で複数のトゥイーンをタイムラインにチェーンすることもできます。
##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プレイグラウンドを確認してください。
BlessingKrofeghaによるペン[ReactGSAP(Easing with Timeline)デモ](https://codepen.io/smashingmag/pen/zYqaEmE)を参照してください。
GSAPの基本的な構成要素のいくつかを理解したので、次のセクションで、典型的なReactアプリで完全なアニメーションを作成する方法を見てみましょう。 フライトを始めましょう!
ReactとGSAPを使用してアニメーション化されたランディングページを構築する
Reactアプリをアニメーション化してみましょう。 依存関係をインストールするために、 npm installを開始して実行する前に、リポジトリのクローンを作成してください。
私たちは何を構築していますか?
現在、ランディングページには、白い背景のテキストがいくつか含まれています。メニューはドロップダウンせず、実際にはアニメーションがありません。 以下は、ランディングページに追加するものです。
- ホームページのテキストとロゴをアニメーション化して、コンポーネントをマウントしたときに簡単に表示できるようにします。
- メニューをアニメーション化して、メニューをクリックするとドロップダウンするようにします。
- ページがスクロールするときに、ギャラリーページの画像を
20degせます。

codesandboxのデモをチェックしてください。

ランディングページのプロセスをコンポーネントに分割するので、理解しやすくなります。 プロセスは次のとおりです。
- アニメーションメソッドを定義し、
- テキストとロゴをアニメーション化、
- トグルメニュー、
- ページスクロールで画像を
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%変換)などの値を割り当て、オブジェクトに不透明度を与えず、オブジェクトを-1でscaleし、オブジェクトを2secでeaseに戻します。
これが機能するかどうかを確認するには、 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を使用していました。 値がnullに設定されている変数introを作成しました。 次に、 useEffectフック内で、 textIntroメソッドとintro変数を呼び出しました。 ホームコンポーネント内のh5タグで、 ref propを定義し、 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フック内で、 useEffectフックを使用してページの変更をuseHistoryしました。ページが変更された場合は、 clickedとmenuNameの状態値をそれぞれfalseとMenuに設定しました。
メニューを処理するために、初期状態の値がfalseであるかどうかを確認し、trueの場合、 initial 、 clicked 、 menuNameの値をnull 、 true 、 Closeに変更します。 それ以外の場合は、ボタンがクリックされているかどうかを確認します。trueの場合は、 menuNameをMenuに変更します。 次に、ボタンがクリックされたときにボタンを1sec無効にするdisabledMenu関数があります。
最後に、 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度傾斜させ、メニューを緩和し、 2degreesプロパティを使用しstaggerアニメーションをオフセットし、メニューをright to topに0.7秒で変換します。同じプロパティが0.7sec関数にも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コンポーネントで行ったことは、 menuShow 、 menuHide 、およびtextIntroであるアニメーション関数をインポートすることでした。 次に、 refsフックを使用してDOM要素に作成された各参照に変数を割り当て、値としてnullをuseRefました。 useEffectフック内で、 menuの状態をチェックします。 clickedがfalseの場合はmenuHide関数を呼び出し、 clickedされた状態がtrueの場合はmenuShow関数を呼び出します。 最後に、関係するDOM要素に、 menuWrapper 、 show1 、 show2という特定のrefsが渡されるようにしました。 これで、メニューがアニメーション化されました。
それがどのように見えるか見てみましょう。

実装する最後のアニメーションは、ギャラリー内の画像がスクロールするときに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に設定しました。
スキューを計算し、 20degsを超えないようにするclamp変数を宣言しました。 ScrollTriggerオブジェクト内で、 triggerプロパティをelem1 paramに割り当てました。これは、この関数を呼び出すときにトリガーされる必要がある要素です。 onUpdateコールバック関数があり、その中には現在の速度を計算して300で除算するvelocity変数があります。
最後に、他の値を設定して、現在の値から要素をアニメーション化します。 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> ) } .... ここでは、。 skewImage skewGalley ./components/Animateしました。 useEffectフック内で、 skewGallery関数を呼び出し、 skewImageをパラメーターとして渡しました。 最後に、 skewImageをref属性に渡しました。
あなたは私に同意するでしょう、それはこれまでのところとてもクールな旅でした。 これがCodeSanboxのプレビューです
この記事のサポートリポジトリはGithubで入手できます。
結論
ReactプロジェクトでGSAPの効力を調査しましたが、この記事では表面をかじっただけです。アニメーションに関しては、GSAPでできることに制限はありません。 GSAPの公式ウェブサイトでは、メソッドとプラグインを完全に理解するのに役立つ追加のヒントを提供しています。 人々がGSAPで行ったことにあなたの心を吹き飛ばすであろう多くのデモがあります。 コメントセクションでGSAPの使用経験をお聞かせください。
資力
- GSAPドキュメント、GreenSock
- 「GreenSockアニメーションプラットフォームの初心者向けガイド」、Nicholas Kramer、freeCodeCamp
- 「GreensockAnimationAPI(GSAP)を使用したアニメーションの概要」、Zell Liew
