🌊

react-springでシンプルなテキストアニメーションをつくる

2022/10/20に公開

今回はシンプルな記述でリッチなUIを作るための、react-springというライブラリをご紹介します。完成系は、以下のように連続したテキストが時間差でIN/OUTされるアニメーションです。

Next.jsプロジェクトへの組み込みを想定して、順に説明していきます。

react-springとは

react-springは他のアニメーションライブラリであるanimatedreact-motionに触発されたUIアニメーションライブラリです。webやネイティブアプリなどあらゆるプラットフォームに対応しており、2022年3月現在もコミュニティによって活発に開発が進められています。

インストール

react-springはユースケース別にモジュールがエクスポートされています。そのため、プロジェクト要件によってはモジュール全体をインストールする必要がなく、容量を抑えることができます。

// 以下のターゲットに対応
@react-spring/konva
@react-spring/native
@react-spring/three
@react-spring/web
@react-spring/zdog

今回は@react-spring/webの範囲内を取り扱うため、以下のように追加します。

yarn add @react-spring/web

アニメーションの実装

時間差で動くアニメーションのために、useTrailフックを利用します。第一引数にはアニメーション対象、第二引数にアニメーションの設定を渡します。

import { useTrail, animated as a } from "@react-spring/web"

// アニメーション対象と設定の初期化
const items = Array(10).fill("chot Inc.")
const config = { mass: 4, tension: 2000, friction: 200 };

const Page: NextPage = () => {
	// useTrailフックを呼び出し、アニメーション対象と設定を渡す
	const trail = useTrail(items.length, {
	  config,
	  opacity: toggle ? 1 : 0,
	  x: toggle ? 0 : 20,
	  height: toggle ? 80 : 0,
	  from: { opacity: 0, x: 20, height: 0 }
	})
}

viewへの割り当て

trailオブジェクトはアニメーションの挙動を含む配列ですので、.map()を使用して展開します。
.to()は同様の機能で.interpolations()が存在しますが、現在非推奨[1]となっておりますので注意しましょう。

return (
  <div className={styles["trails-main"]} onClick={() => set(state => !state)}>
    <div>
      {trail.map(({ x, height, ...rest }, index) => {
        return (
          <a.div
            key={items[index]}
            className={styles["trails-text"]}
            style={{
              ...rest,
              transform: x.to(x => `translate3d(0,${x}px,0)`)
            }}
          >
            <a.div style={{ height }}>{items[index]}</a.div>
          </a.div>
        );
      })}
    </div>
  </div>
)

最終的なコード

cssで調整し、これまでのコードを組み合わせると最終的に次のような形になります。

import React, { useState } from "react";
import { useTrail, animated as a } from "@react-spring/web";
import styles from '../styles/pages/index.module.scss'
import { NextPage } from "next";

const items = Array(10).fill("chot Inc.")
const config = { mass: 4, tension: 2000, friction: 200 };

const Page: NextPage = () => {
  const [toggle, set] = useState(true);
  const trail = useTrail(items.length, {
    config,
    opacity: toggle ? 1 : 0,
    x: toggle ? 0 : 20,
    height: toggle ? 80 : 0,
    from: { opacity: 0, x: 20, height: 0 }
  });

  return (
    <div className={styles["trails-main"]} onClick={() => set(state => !state)}>
      <div>
        {trail.map(({ x, height, ...rest }, index) => {
          return (
            <a.div
              key={items[index]}
              className={styles["trails-text"]}
              style={{
                ...rest,
                transform: x.to(x => `translate3d(0,${x}px,0)`)
              }}
            >
              <a.div style={{ height }}>{items[index]}</a.div>
            </a.div>
          );
        })}
      </div>
    </div>
  )
}

export default Page

まとめ

公式ドキュメントを参照すると、useTrail以外にも様々な表現が可能となるAPIがサンプル付きで豊富に載っています。ぜひ手を動かして試してみてください。

脚注
  1. https://react-spring.io/common/interpolation ↩︎

chot Inc. tech blog

Discussion