react-springでシンプルなテキストアニメーションをつくる
今回はシンプルな記述でリッチなUIを作るための、react-springというライブラリをご紹介します。完成系は、以下のように連続したテキストが時間差でIN/OUTされるアニメーションです。
Next.jsプロジェクトへの組み込みを想定して、順に説明していきます。
react-springとは
react-springは他のアニメーションライブラリであるanimated、react-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がサンプル付きで豊富に載っています。ぜひ手を動かして試してみてください。
ちょっと株式会社(chot-inc.com)のエンジニアブログです。 フロントエンドエンジニア募集中! カジュアル面接申し込みはこちらから chot-inc.com/recruit/iuj62owig
Discussion