7839

雑草魂エンジニアブログ

【React】React + GSAP(TextPlugin)で1文字ずつ表示されるアニメーションを実装する(Next.js)

先日に引き続き、今回も GSAP で実装したアニメーションをご紹介。 今回は、タイプライター風アニメーションというのか、1 文字ずつ文字が表示されるアニメーションを実装した。

f:id:serip39:20201222214133g:plain

TextPlugin

greensock.com

TextPlugin は、GSAP のプラグインの 1 つである。このプラグインの特徴は以下の通りである。

  • DOM要素にテキストコンテンツを 1 文字ずつ埋め込む(区切り文字をスペースなどに設定することで、1 単語ごとに埋め込むこともできる)
  • アニメーションが終了すると、DOM要素にはテキストが埋め込まれて、完全に置き換えられる
  • 表示速度を設定できる

使い方

import { gsap } from 'gsap'
import { TextPlugin } from 'gsap/TextPlugin'

useEffect(() => {
  if (process.browser) {
    gsap.registerPlugin(TextPlugin)
    setAnimation()
  }
}, [])

const setAnimation = () => {
  gsap.to("#テキストを挿入するDOM要素", {
    duration: 2, //アニメーション時間(秒)
    text: {
      value: "This is the new text", //表示するテキスト
      delimiter: "",  //区切り文字
    },
    ease: "ease",  // アニメーションのタイミング・進行割合を指定する
  })
}

詳細は、公式ドキュメント を参照して欲しい。

React での実装例

React で実装するにあたり、TextAnimationコンポーネントを作成した。

テキストを埋め込む領域を確保しておかないと、改行されるたびに表示領域が大きくなるので、事前に Height を確保することにした。処理の流れは以下の通りである。

  1. DOMがレンダリングされる
  2. DOM内のテキストを読み込む
  3. DOMの高さを取得する
  4. DOM内のテキストを削除し、高さを設定する
  5. scrollTrigger をトリガーとして、テキストアニメーションを行う
import { ReactElement, useCallback } from 'react'
import { gsap } from 'gsap'
import { TextPlugin } from 'gsap/TextPlugin'
import { ScrollTrigger } from 'gsap/ScrollTrigger'

type Props = {
  children: React.ReactNode
  section: string
}

const TextAnimation = (props: Props): ReactElement => {
  const textRef = useCallback((node) => {
    if (node !== null) {
      const text = node.innerHTML  //テキストを読み込む
      const height = node.clientHeight  //高さを取得する
      node.innerHTML = ''  //テキストを削除する
      node.style.height = height + 'px'  //高さを設定する
      setAnimation(text)
    }
  }, [])

  const setAnimation = (text) => {
    const numText = text.length
    const selector = '#' + props.section

    gsap.registerPlugin(TextPlugin)
    gsap.registerPlugin(ScrollTrigger)
    gsap.to(`${selector} .animation-text`, {
      duration: numText * 0.03,
      text: {
        value: text,
      },
      ease: 'none',
      scrollTrigger: {
        trigger: selector,
        start: 'top 40%',
        end: 'bottom 40%',
      },
    })
  }

  return (
    <p ref={textRef} className="animation-text">
      {props.children}
    </p>
  )
}

export default TextAnimation

今回、useCallback を使って、コールバック形式の ref を使っている。最初は、useRef を使って実装したが、その場合に TextAnimation のコンポーネントを使い回すことができなかった。(どのコンポーネントでも、同じテキストが表示されてしまった。)

useRef の特徴は以下の通りである。

  • useRef は毎回のレンダーで同じ ref オブジェクトを返す。
  • useRef は中身が変更になってもそのことを通知しない。.current プロパティを書き換えても再レンダーは発生しない。

詳細は、公式ドキュメント useRef 参照。

実際に、TextAnimation を使う場合は、以下のようにする。

import { ReactElement } from 'react'
import TextAnimation from '~/components/TextAnimation'

const Component = (): ReactElement => {
  return (
    <section id="souseki">
      <TextAnimation section="souseki">
        吾輩は猫である。
        <br />
        名前はまだ無い。どこで生れたかとんと見当がつかぬ。
        <br />
        何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。
        <br />
        吾輩はここで始めて人間というものを見た。
        <br />
        しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。
        <br />
        夏目漱石
      </TextAnimation>
    </section>
  )
}

export default Component

f:id:serip39:20201222214133g:plain

まとめ

GSAP の TextPlugin の使い方を簡単に紹介した。簡単にタイプライター風のアニメーションが実現できて、便利であった。

また、今回の実装を通して、React の useRef やコールバック形式の ref についても学ぶことができた。

それでは、ステキな開発ライフを。

関連する記事

serip39.hatenablog.com