旧ブログ(ISSEN)から移行しました

【React/GSAP】useGSAPで作るテキストアニメーション

【React/GSAP】useGSAPで作るテキストアニメーション

杉田侑祐
杉田侑祐7分で読めます

はじめに

この記事の概要

こんにちは、株式会社TOKOSのスギタです!
今回はReact/GSAPのuseGSAPを使用してテキストアニメーションを作ってみました。
また今回概要も調べてみたので、概要の説明もしていきます。

対象読者

  • WEB制作を行っている方

今回の完成予定

今回は以前作ったテキストアニメーションをReact/GSAPで再現したいと思います。
以前作ったテキストアニメーションは下記の記事を参照してください!

【GSAP】コピペでOK!!GSAPテキストアニメーションの実装

GSAPとSplitTypeを使って、一文字ずつ遅れて表示されるテキストアニメーションを実装する方法を解説します。コピペで使える実践的なサンプルコード付き。

開発環境

  • react: 18.2.0,
  • gsap: 3.12.5,

実装

React/GSAPの導入

今回はyarnを使用します。

まずはGSAPのインストールをします。

$ yarn add gsap

次にReact/GSAPのパッケージのインストールです。

$ yarn add @gsap/react

React/GSAPの概要

まずは基本的な構文からです。
例は下記になります。

JavaScript
import { useRef } from "react"
import gsap from "gsap"
import { useGSAP } from "@gsap/react"
 
const container = useRef()
 
useGSAP(
  () => {
    //ここにgsapの記述
    gsap.to(".box", { x: 360 })
  },
  { scope: container },
)

基本的にReact/GSAPではuseGSAP内にgsap構文で書いていきます。

今回のメインのuseGSAPというフックですが、これが何をしているかというと、useEffectを踏襲したフックみたいです。
このuseEffectではクリーンアップ関数の記述が重要ですが、useGSAPにはgsap.context()が内包されていてgsapで記述されている関数にはクリーンアップ関数の記述をせずにDOMの操作ができるみたいです。

gsap.context() | GSAP | Docs & Learning

If you're using React, we've brought out a hook that abstracts away gsap.context() and handles animation cleanup for you!

gsap.com

またuseGSAPの第二引数にはconfigObjectというオブジェクトを入れることができます。

このconfigObjectの概要を説明します。

プロパティ名概要
dependenciesArray/null デフォルト値: [ ]内部のuseEffectに渡される依存配列
scopeReact refuseGSAP()フック内のすべてのGSAPセレクタテキストがそのコンテナの子孫にスコープされるように、configオブジェクトのスコープとしてコンテナを定義します。
revertOnUpdateboolean デフォルト値: false依存関係の配列を定義して依存関係が変更されても、GSAP 関連のオブジェクト(アニメーション、ScrollTriggers など)は元に戻りません。コンポーネントがアンマウントされ、フックが破棄されたときのみ、それらは元に戻ります。フックが再同期するたびに(依存関係が変更されるたびに)コンテキストを元に戻したい場合は、revertOnUpdate: true を設定します。

scopeがめちゃくちゃ便利なので別途解説します。

refをスコープとして渡すと、useGSAPフック内のGSAP関連コードで使われるすべてのセレクタテキストもそれに応じてスコープされます。つまり子孫にuseRefでアクセスしなくてよくなります。

1.スコープにRefを渡さない例

JavaScript
const container = useRef()
const box1 = useRef()
const box2 = useRef()
const box3 = useRef()
 
useGSAP(() => {
  gsap.from([box1, box2, box3], { opacity: 0, stagger: 0.1 })
})
 
return (
  //各要素にuseRefを用いてアクセスする
  <div ref={container}>
    <div ref={box1} className="box"></div>
    <div ref={box2} className="box"></div>
    <div ref={box3} className="box"></div>
  </div>
)

2.スコープにRefを渡す例

JavaScript
const container = useRef()
 
useGSAP(
  () => {
    gsap.from(".box", { opacity: 0, stagger: 0.1 })
  },
  { scope: container },
) // 親のRefをスコープにわたす
 
return (
  <div ref={container}>
    //子孫はuseRefを用いなくてもよい
    <div className="box"></div>
    <div className="box"></div>
    <div className="box"></div>
  </div>
)

このように親要素のrefを渡すだけで子要素のrefにアクセスせずに済みます。

またuseGSAPには参照し使用できる機能があります。

プロパティ名概要
contextgsap.context()すべてのアニメーションを追跡するインスタンス。
contextSafe関数の実行中に作成された GSAP 関連のオブジェクトは、Context が元に戻されたときに元に戻ります(クリーンアップ)。コンテキストセーフ関数内のセレクタテキストも Context のスコープを使用します。 contextSafe() は関数を受け取り、その関数の新しいコンテキストセーフバージョンを返します。

コンテキストセーフについて

useGSAPの内部のgsap.contextに追加されている状態、クリーンアップされる状態という解釈。(違ったらすみません)

コンテキストセーフでは無い例

JavaScript
const container = useRef()
 
//下記はコンテキストセーフ
useGSAP(
  () => {
    gsap.to(".good", { x: 100 })
  },
  { scope: container },
)
 
//下記が悪い例、クリーンアップできていない
const onClickBad = () => {
  gsap.to(".bad", { y: 100 })
}
 
return (
  <div ref={container}>
    <div className="good"></div>
    <button onClick={onClickBad} className="bad"></button>
  </div>
)

コンテキストセーフさせるにはcontextSafe()wrapしなければいけません。

contextSafe()の使用の仕方は2種類あります。

1.デストラクチャリングを用いて関数を抽出する

JavaScript
const container = useRef()
 
//デストラクチャリングを用いて関数を抽出する
const { contextSafe } = useGSAP({ scope: container })
 
//contextSafe()でwrap
const onClickGood = contextSafe(() => {
  gsap.to(".good", { rotation: 180 })
})
 
return (
  <div ref={container}>
    <button onClick={onClickGood} className="good"></button>
  </div>
)

2.引数を使用

JavaScript
const container = useRef()
const badRef = useRef()
const goodRef = useRef()
 
useGSAP(
  (context, contextSafe) => {
    gsap.to(goodRef.current, { x: 100 })
 
    //コンテキストセーフではない例
    badRef.current.addEventListener("click", () => {
      gsap.to(badRef.current, { y: 100 })
    })
 
    //第二引数のcontextSafe()でwrap
    const onClickGood = contextSafe(() => {
      gsap.to(goodRef.current, { rotation: 180 })
    })
 
    goodRef.current.addEventListener("click", onClickGood)
 
    //イベントリスナーのクリーンアップ関数
    return () => {
      goodRef.current.removeEventListener("click", onClickGood)
    }
  },
  { scope: container },
)
return (
  <div ref={container}>
    <button ref={badRef}></button>
    <button ref={goodRef}></button>
  </div>
)

以上がcontextSafe()の使用方法です。

詳しく知りたい方は下記を参照してください。

React &amp; GSAP | GSAP | Docs &amp; Learning

Animate with ease in React - our useGSAP() hook handles all animation cleanup for you.

gsap.com

テキストアニメーションの実装

今回は前回とは違いSplitTextは使用しません。

JSX
<div ref={textWrapper} className="text-wrapper">
  <span className="text">P</span>
  <span className="text">L</span>
  <span className="text">A</span>
  <span className="text">Y</span>
  <span className="text">T</span>
  <span className="text">O</span>
  <span className="text">E</span>
  <span className="text">N</span>
  <span className="text">J</span>
  <span className="text">O</span>
  <span className="text">Y</span>
</div>
CSS
.text-wrapper {
  color: #fff; /*お好きなフォントカラーを選んでください*/
  font-size: 80px; /*お好きなフォントサイズを選んでください*/
  font-family: "Times New Roman", Times, serif; /*お好きなフォントを選んでください*/
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);
  line-height: 1; /*line-heightなくしたほうがカッコ良くなります*/
}
 
.text {
  transform: translateY(100px);
  display: inline-block; /*インライン要素の為*/
  transition: 0.5s;
}
JavaScript(GSAP)
export const TextAnimation = () => {
  const textWrapper = useRef()
 
  useGSAP(
    () => {
      gsap.to(".text", {
        delay: 0.2,
        duration: 0.5,
        ease: "power2.out", //型がリテラル型なので注意
        stagger: 0.05,
        y: 0,
      })
    },
    //scopeで親を指定すると子孫はuseRefでアクセスせずそのまま操作できる
    { scope: textWrapper },
  )
 
  return (
    <div ref={textWrapper} className="text-wrapper">
      <span className="text">P</span>
      <span className="text">L</span>
      <span className="text">A</span>
      <span className="text">Y</span>
      <span className="text">T</span>
      <span className="text">O</span>
      <span className="text">E</span>
      <span className="text">N</span>
      <span className="text">J</span>
      <span className="text">O</span>
      <span className="text">Y</span>
    </div>
  )
}

これで完成になります。
第二引数のscopeに親要素のrefを渡すだけでめちゃくちゃ簡単に書けます。

一応参考動画も載せておきます。

使用しているメソッド等がわからない方は下記を参照してください。

【GSAP】コピペでOK!!GSAPテキストアニメーションの実装

GSAPとSplitTypeを使って、一文字ずつ遅れて表示されるテキストアニメーションを実装する方法を解説します。コピペで使える実践的なサンプルコード付き。

さいごに

今回はReact/GSAPを紹介しました。
このuseGSAP()を用いた記述のほうが直感的で簡単に書けてとても良いと感じました。
私としてはReactライクな記述方法のほうが書きやすく感じました!
また関係ないのですが、公式ドキュメントが更新されていてめちゃくちゃ読みやすくなっていることに感動しました。
ライブラリを使用する際はドキュメントの読みやすさも重要だなとあらためて感じました。

この記事を書いた人

杉田侑祐
杉田侑祐

TOKOSのフロントエンドエンジニア兼UI/UXデザイナー。このブログではフロントエンドメインで投稿しています。HIPHOPとゲームが好きです✌️