Framer Motionでアコーディオンアニメーション実装すると楽
以前、Framer Motion を試してみた記事を書いたのですが、その続編で、アコーディオンのアニメーションを実装してみました。高さが変わってアコーディオンが開くアニメーションです。
実施環境
項目 | 詳細 |
---|---|
PC | MacBook Pro(14 インチ、2021)Apple M1 Pro |
OS | MacOS Monterey 12.5 |
Node.js | v16.14.2 |
Next.js | v12.1.0 |
React.js | v17.0.1 |
Framer Motion | v7.0.0 |
Framer Motion を使わずに実装
開いたあとの高さがわかっている場合
高さがわかっている場合のアニメーションは、CSS(一部クラス切り替えのみ js)で簡単に実装できます。アニメーション開始前、終了後の高さを指定してあげて、transition を記述すれば OK です。
.accordion {
/* 最初の高さ */
height: 50px;
transition: 1s;
...
&.-open {
/* 開いたあとの高さ */
height: 130px;
}
}
開いたあとの高さがわかっていない場合
ただ、前後の高さが固定のケースは実際あまりなく、height は auto 等を指定したいかと思います。が、アニメーション終了後を auto 等にすると transition が効きません。
.accordion {
/* 最初の高さ */
height: 50px;
transition: 1s;
...
&.-open {
/* 開いたあとの高さ */
height: auto;
}
}
それに対する CSS での解法としては、max-height を使うなどがあるようです。
.accordion {
/* 最初の高さ */
max-height: 50px;
transition: 1s;
...
&.-open {
/* 開いたあとの高さ */
max-height: 100vh;
}
}
ただしこの場合、transition 時間を例えば 1s とした場合、アニメーションは 1s で 100vh になるように実行されます。そのため、サンプルでもわかるように、開く時は 1s にしては早すぎ、閉じる時は最初の数ミリ秒アニメーションが開始されません。あと、高さが 100vh を超える場合は 100vh までしか開きません。100vh ではなく 9999px とかありえないほど大きい高さを指定する方法も考えられますが、その場合前述のアニメーション時間のバグがひどくなります。
そのため、そのへんも考慮した場合は js 等で予め高さを取得してあげるしか無い気がします。
Framer Motion
Framer Motion を使えば、100%や auto などを設定しても、よしなにアニメーションしてくれます。
公式のサンプル
実装例
公式だと表示、非表示の出し分けだったので、上記 codepen と同じようなタイプの実装をしてみました。
import React, { useState } from 'react';
import styles from './index.module.css';
import { motion, useAnimationControls } from 'framer-motion';
const TestPageAccordion = () => {
const [isExpanded, setIsExpanded] = useState(false);
const toggleExpanded = () => {
if (isExpanded) {
controls.start({
height: '50px',
});
} else {
controls.start({
height: '100%',
});
}
setIsExpanded(!isExpanded);
};
const controls = useAnimationControls();
return (
<div className={styles['main-contents']}>
<h1>Accordion</h1>
<button onClick={toggleExpanded}>open</button>
<motion.div
className={styles.accordion}
animate={controls}
transition={{ duration: 1 }}
>
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
</motion.div>
</div>
);
};
export default TestPageAccordion;
まとめ
アコーディオンのアニメーションって結構難しいと感じてたのですが、Framer Motion を使えばかなり楽に実装できました。ぜひ試してみてください。
Discussion