React + tailwind css で画面表示されたら下の方からシュッと出てくるやつを実装
概要
React + tailwindcss のプロジェクトで、LPとかでよく見る「要素が画面内に入ったらシュッとフェードインする」挙動を実現したいと思いました。ググって引っかかってくる知見は jQuery をつかおうみたいな記事ばかりだったので、個人開発で現在使用している[1] Next.js + tailwindcss での構成でいい感じにできた実装を共有します。どなたかの参考になれば幸いです。
Tl;dr
以下の実装で AwesomeHogeComponent
が画面に表示されたら下からシュッとフェードインする挙動を実現できるFadeInBottom
コンポーネントを作成できました。
const AwesomeFadeInComponent: React.FC = () => {
return (
<FadeInBottom>
<AwesomeHogeComponent />
</FadeInBottom>
);
};
使用例
以下のページで使用しています。
#パッチワーク2023 出演者情報 | patchwork official site
実装手順
tailwind.config.js にフェードインアニメーションのクラス名を定義する
まず、フェードインアニメーションを tailwindcss のカスタムクラスとして追加します。
tailwind.config.js に記載できるフェードインのアニメーション自体は Animasta [2] を使って作成しました。ここで作成した気に入ったエフェクトを tailwind.config.js に追加します。
module.exports = {
// 中略
theme: {
extend: {
// 中略
// Animista で好みに生成したものを貼り付ける
//FreeBSD-licensed CSS animation by Animista
animation: {
"fade-in-bottom": "fade-in-bottom 0.6s ease-out both",
},
keyframes: {
"fade-in-bottom": {
"0%": {
transform: "translateY(50px)",
opacity: "0",
},
to: {
transform: "translateY(0)",
opacity: "1",
},
},
},
},
},
};
react-intersection-observer
を導入する
目的の動きを実現させるには、画面内に入ったタイミングで処理を発火させる必要があります。そのような処理が可能なライブラリとして react-intersection-observer
[3] がありましたので、これをインストールします。
yarn add react-intersection-observer
コンポーネントにクラス名を付与する Wrapper コンポーネントを作成する
最後に、コンポーネントが画面内に表示されたら受け取ったコンポーネントにアニメーションを加える Wrapper コンポーネントを作成します。
実装したものが以下です。この FadeInBottom
で任意のコンポーネントを囲むとそのコンポーネントにが画面内に表示されたら下の方からシュッと出てくるアニメーションが付与されます。
import React from "react";
import { useInView } from "react-intersection-observer";
type Props = {
children: React.ReactNode;
};
/**
* 子要素を下からフェードインさせる
*
* @example
* ```tsx
* <FadeInBottom>
* <div>フェードインする要素</div>
* </FadeInBottom>
* ```
*/
export const FadeInBottom: React.FC<Props> = ({ children }) => {
const { ref, inView } = useInView({
// ref要素が現れてから50px過ぎたら
rootMargin: "-50px",
// 最初の一度だけ実行
triggerOnce: true,
});
const fadeInClassName = inView ? "animate-fade-in-bottom" : "opacity-0";
const wrappedChildren = React.Children.map(children, child => {
if (React.isValidElement(child)) {
const className = [child.props.className, fadeInClassName]
.filter(el => el)
.join(" ");
return React.cloneElement(child as React.ReactElement, {
ref,
className,
});
} else {
return child;
}
});
return <>{wrappedChildren}</>;
};
画面内に表示されたら処理を発火する
画面内に表示されたら処理を発火する動きには、前の行程でインストールした react-intersection-observer
を使用します。useInView
をインポートして ref, inView を受け取ります。refを対象となるコンポーネントに渡すと、inView にはそのコンポーネントが画面内に現れた時に true になる boolean 値が入ってきます。この boolean 値を利用して画面内表示契機での処理を実現できます。
フェードイン処理
前の工程で tailwind.config.js
にフェードインアニメーションの設定を追加しましたが、この設定の場合コンポーネントの className に animate-fade-in-bottom
が付与すると下からフェードインする挙動になります。このクラスとコンポーネントを透明にする opacity-0
、前述の inView を組み合わせることで、画面内に表示されたらフェードインされるアニメーションを実現できます。具体的には、目的の要素に inView が false のときは opacity-0
, true のときは animate-fade-in-bottom
を付与します。
またこの時、useInView
には triggerOnce を設定しているので一度表示されたものはその後 opacity-0
に戻らない(一度表示されたコンポーネントはリロードしない限り非表示には戻らない)ようになっています。
終わりに
囲うだけで楽にシュッとエフェクトを実現できるようになりました。調子に乗ってやりすぎるとパフォーマンスに影響あるかも。知らんけど。
-
#patchwork2022 の運営側を色々システム化したあれこれ - Qiita で紹介している、ホームページで使用しています ↩︎
Discussion