かっこいいアニメーションを実装できるFramer Motionを触ってみた

この記事は Tech KAYAC Advent Calendar 2021 の 10 日目の記事です。

こんにちは! 今年の4月に入社した片岡です。普段は、クライアントワークの案件開発をするフロントエンドエンジニアをやっております。入社してまもなく1年ですがこれまでNext.jsをベースとしたwebサービスやwebサイトの開発をしてきました。

この記事では、Reactでかっこいいアニメーションを実装できるライブラリについて紹介します。

Framer Motion

Framer Motionは、オープンソースで開発されているReact用のモーションライブラリです。ページ遷移時のアニメーションや、ユーザのジェスチャーに合わせたモーションを比較的簡単に実装することができるものです。

www.framer.com

この記事を書きながら実際にアニメーションを作ってみます。公式ドキュメントを見ながらやっていきます。

Animations

まずは、基本的な書き方をしてみます。

import { motion } from "framer-motion";
import styled from "styled-components";

const Box = styled(motion.div)`
  width: 80px;
  height: 80px;
  background: green;
  border-radius: 20px;
`;

const Hello = () => {
  return <Box animate={{ scale: 3 }} />;
};

export default Hello;

animate={{ scale: 3 }}を指定すると、ページ表示時に Box が拡大します。CSSアニメーションを書かなくても簡単に実装ができました。

animate={{ x: [0, 200, 0], rotate: 90 }} transition={{ duration: 3, repeat: Infinity }}をpropsで渡すと、移動しながら回転してを繰り返すようになりました。animateのプロパティはキーフレームを指定する感じで配列でパラメータを書くことができます。

Gestures Animations

クリックやドラッグの入力によってアニメーションすることはよくありますが、Framer Motionでも簡単に実装が可能です。

様々なAPIがあり、ホバーしている間やクリックしている間などにscaleの変更ができたりします。以下の例では、カーソルを合わせると拡大します。

<Box whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} />

ホバージェスチャーでのトリガーは3つの props があり、whileHoverの他にonHoverStart(event, info)onHoverEnd(event, info)があります。

Gestures Animations は、Hover、Focus、Tap、Drag などがあります。

Variants

バリアントは、サブツリーのコンポーネントを1つのpropsでアニメーションをすることができたり、アニメーションのタイミングを少しずつ遅延させることができます。

試しに以下のようなものを作ってみました。

f:id:ktpi2000:20211209160500g:plain

コードの一部は次のとおりです。

const container = {
  hidden: { rotate: -90 },
  show: {
    rotate: 0,
    transition: {
      staggerChildren: 0.1,
      delayChildren: 0.3,
    },
  },
};

const itemA = {
  hidden: { scale: 0, top: 40 },
  show: { scale: 1, top: 40 },
};

const itemB = {
  hidden: { scale: 0, top: 110 },
  show: { scale: 1, top: 110 },
};

const Variants = () => {
  const [count, setCount] = useState(0);

  return (
    <Container>
      <Box initial="hidden" animate="show" variants={container} key={count}>
        <Circle variants={itemA} />
        <Circle variants={itemA} />
        <Circle variants={itemB} />
        <Circle variants={itemB} />
      </Box>
      <Button onClick={() => setCount(count + 1)}>Retry</Button>
    </Container>
  );
};

Boxコンポーネントの下にCircleコンポーネントが4つ並んでいます。Boxコンポーネントに渡しているvariants propsでは、ページ表示時にrotateを行い、transitionで各Circleコンポーネントを 0.1 秒遅延させて表示する(staggerChildren)、Boxコンポーネントのアニメーションが終了してから Circle コンポーネントのアニメーションを開始するまでの遅延時間(delayChildren)の記述をしていしています。

Circleコンポーネントには、スケール等の記述をしています。

アニメーションをボタンのクリックで実行させるときは、motionコンポーネント(例では Box コンポーネント)にpropsしているkeyの値を変更することで実装が可能です。keyの値が変わることで別のコンポーネントという扱いとなり、古いコンポーネントはアンマウントされます。他の方法として、Framer MotionにはuseAnimation()というhooksがあり、このhooksを用いることでアニメーションを制御することも可能です。

最後に

簡単ではありましたが、公式ドキュメントを見て気になったものを実際に書いてみました。CSSアニメーションを地道に書かなくてもリッチなアニメーションを実装できるのはとても魅力的に感じました。また、今回はstyled componentsと組み合わせて書いてみました。組み合わせて書くことは難しい作業ではなかったので導入しやすいかと感じました。

アニメーションが多すぎるとユーザ体験が低下しますが、適切に導入することでリッチな体験ができるサイトを作ることができます。今後もFramer Motionのようなアニメーションライブラリの知識を習得してWebフロントエンドの開発をしていきたいです。

試しに書いてみたリポジトリを下に貼っておきます

github.com

参考

www.framer.com