【React】React×TailwindCSSでアニメーションを実装する

ReactとTailwindCSSでアニメーションを実装しようとして少し詰まったのでメモ。

「ボタンが押されたら1秒かけて文字の不透明度を0から1にする」というアニメーションは下記のようにすれば実現できます。

import { useState } from "react";

export default function Home() {
  const [animationStart, setAnimationStart] = useState(false);

  return (
    <>
      <button
        onClick={() => {
          setAnimationStart(true);
        }}
      >
        サンプルボタン
      </button>
      <div
        className={`transition-all duration-1000 ease-linear ${
          animationStart ? "opacity-100" : "opacity-0"
        }`}
      >
        サンプル
      </div>
    </>
  );
}

「サンプル」のdivタグにtransition-all duration-1000 ease-linearでアニメーションが完了する時間や進行割合などを指定しています。

さらに、animationStart ? "opacity-100" : "opacity-0"animationStartは初期表示時はfalseなので、opacity-0が適用されて文字列「サンプル」は初期表示時は表示されません。

「サンプルボタン」が押されたらanimationStartがtrueになり、「サンプル」のdivタグのclassNameがopacity-0からopacity-100になるので、1秒かけて文字列「サンプル」が表示されます。

アニメーションをしたい要素が常にDOMに存在すれば上記のような書き方でOKですが、問題なのは下記のような場合です。

import { useState } from "react";

export default function Home() {
  const [animationStart, setAnimationStart] = useState(false);

  return (
    <>
      <button
        onClick={() => {
          setAnimationStart(true);
        }}
      >
        サンプルボタン
      </button>

      {animationStart && (
        <div
          className={`transition-all duration-1000 ease-linear ${
            animationStart ? "opacity-100" : "opacity-0"
          }`}
        >
          サンプル
        </div>
      )}
    </>
  );
}

上記のコードでは、animationStartがtrueになるまでは「サンプル」のdivタグはDOMに存在せず、trueになると初めてdivが生成されます。

この場合、アニメーションが適用されずに即座に最終状態が表示されるようです。
※ChatGPTに聞きました

じゃあどうするかと調べたら、CSSのanimationkeyframesを使用したら実現できました。

/** @type {import('tailwindcss').Config} */
module.exports = {
  mode: "jit",
  darkMode: false,
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      keyframes: {
        sample: {
          from: { opacity: 0 },
          to: { opacity: 1 },
        },
      },
      animation: {
        sample: "sample 1s linear",
      },
    },
  },
  plugins: [],
};
import { useState } from "react";

export default function Home() {
  const [animationStart, setAnimationStart] = useState(false);

  return (
    <>
      <button
        onClick={() => {
          setAnimationStart(true);
        }}
      >
        サンプルボタン
      </button>

      {animationStart && <div className={"animate-sample"}>サンプル</div>}
    </>
  );
}

tailwind.config.jsにanimationkeyframesを定義しています。
animationにはアニメーションの名前、アニメーションの秒数、進行割合を指定しています。
keyframesにはアニメーションの始まりと終わりを定義しています。

ReactのコンポーネントではdivタグのclassNameにanimate-sampleを指定することにより、tailwind.config.jsで定義したアニメーションのクラスが適用されます。

これにより、animationStartがfalseの間はdivタグはDOMに存在せず、trueになったらdivタグが生成され、かつ1秒かけて「サンプル」の文字が表示されるアニメーションが実現できました!

最初は「React TailwindCSS アニメーション」みたいな感じで調べていて、間違ったアプローチをしていたので少し詰まりました。

「display none アニメーション CSS」みたいな感じで調べたら今回の解決法に行きつきました。

IT技術ブログ
↓↓「にほんブログ村」のランキングに参加しています。少しでも面白い、参考になったとか思われたらポチッとしていただけると嬉しいです!

にほんブログ村 IT技術ブログへ

にほんブログ村