Framer Motionは、本番環境に対応したReact用のモーションライブラリです。構文は宣言的で、複雑なアニメーションのコードも簡潔に書けます。つまり、コードベースが読みやすく、保守しやすいということです。
ライブラリの基本的な使い方については「React + TypeScript: Framer Motionでアニメーションを加える」をお読みください。本稿は、公式サイト「Animation」の「Variants」について解説します。つぎのサンプル001が、公式作例を参考にしたプルダウンメニューです。
サンプル001■React + TypeScript: Framer Motion Variants 01
https://codesandbox.io/s/react-typescript-framer-motion-variants-01-862dkh
コンポーネントごとのアニメーションは、animate
プロパティにオブジェクトで与えられます。けれど、DOMツリー全体に調和したアニメーションを加えたいことがあるでしょう。そのときに用いるのがvariants
プロパティです。
ボタンのアニメーション
variants
には、アニメーションをあらかじめターゲットごとに、オブジェクト(menuButtonVariants
)で定めてください。アニメーションとして参照されるのは、ターゲットのラベル(open
とclosed
)です。
const menuButtonVariants: Variants = {
open: { rotate: 180 },
closed: { rotate: 0 }
};
const ArrowSign: FC = () => {
return (
<motion.div
variants={menuButtonVariants}
transition={{ duration: 0.2 }}
>
<svg />
</motion.div>
);
};
variants
によるアニメーションは、DOMツリーの親からコントロールできます。animate
プロパティの値に与えるのは、variants
に定めたアニメーションのラベルです。つぎのルートコンポーネントは、状態変数値(isOpen
)に応じてvariants
のアニメーションを切り替えます。
export default function App() {
const [isOpen, setIsOpen] = useState(false);
const handleClick = () => setIsOpen(!isOpen);
return (
<motion.nav
initial={false}
animate={isOpen ? 'open' : 'closed'}
>
<Button onClick={handleClick} />
</motion.nav>
);
}
ボタンをクリックするたびに、ボタン上に表示された矢印(svg
)が180度回転するようになりました(前掲サンプル001)。
メニューのアニメーション
メニュー項目を表示するのが<MenuItems>
コンポーネントです。
export default function App() {
const [isOpen, setIsOpen] = useState(false);
return (
<motion.nav
initial={false}
animate={isOpen ? 'open' : 'closed'}
>
<MenuItems isOpen={isOpen} />
</motion.nav>
);
}
variants
を定める基本は変わりません。変数menuVariants
のラベルopen
とclosed
に、それぞれアニメーションのオブジェクトを与えました。なお、clipPath
は、CSSのclip-path
に準じて要素の表示範囲を決めるプロパティです。
const menuVariants: Variants = {
open: {
clipPath: 'inset(0% 0% 0% 0% round 10px)',
transition: {
type: 'spring',
bounce: 0,
duration: 0.7,
delayChildren: 0.3,
staggerChildren: 0.05
}
},
closed: {
clipPath: 'inset(10% 50% 90% 50% round 10px)',
transition: {
type: 'spring',
bounce: 0,
duration: 0.3
}
}
};
export const MenuItems: FC<Props> = ({ isOpen }) => {
return (
<motion.ul
variants={menuVariants}
>
{Array.from(new Array(5), (_, index) => (
<motion.li key={index}>
Item {String(index + 1).padStart(2, '0')}
</motion.li>
))}
</motion.ul>
);
};
メニューボタンクリックで、メニューが開いたり閉じたりするようになりました(前掲サンプル001)。
メニュー項目にも、variants
で簡単なアニメーションを加えましょう(itemVariants
)。垂直方向の移動と透明度の変化です(前掲サンプル001)。これで、プルダウンメニューの作例ができ上がりました。
const itemVariants: Variants = {
open: {
opacity: 1,
y: 0,
transition: { type: 'spring', stiffness: 300, damping: 24 }
},
closed: { opacity: 0, y: 20, transition: { duration: 0.2 } }
};
export const MenuItems: FC<Props> = ({ isOpen }) => {
return (
<motion.ul
variants={menuVariants}
>
{Array.from(new Array(5), (_, index) => (
// <motion.li key={index}>
<motion.li variants={itemVariants} key={index}>
Item {String(index + 1).padStart(2, '0')}
</motion.li>
))}
</motion.ul>
);
};
今回ご紹介したのは、variants
はコンポーネントごとに定め、animate
プロパティに与えるラベルを親のルートコンポーネント(App
)で切り替えるという仕組みでした。variants
にはほかにも応用できる手法があります。詳しくは、公式サイトの「Variants」をご参照ください。