🎥

Framer Motion 3Dで簡単な3Dアニメーションを使ったReactアプリを作る

2023/12/24に公開

この記事は、フラー株式会社 Advent Calendar 2023 の 23 日目の記事です。22 日目は、nirazo さんの 「プログラミング教材を作ろう!Playground Book 概論」でした。

背景

趣味で作っている React アプリでのアニメーションの実装に Framer Motion を使っており、直感的に使える所が気に入っていました。その中で、公式ドキュメントを読んでいると、3D アニメーションを実装できる Framer Motion 3D というライブラリが用意されていることを知り、興味が湧いたので使ってみることにしました。

この記事では、Framer Motion 3D を使ったインタラクティブな 3D アニメーションの簡単な作例を紹介したいと思います。

Framer Motion 3D

Framer Motion 3D とは、Framer 社が提供する、React のための 3D アニメーションライブラリです。 同社のアニメーションライブラリである Framer Motion の機能を、Three.js ラッパーの React Three Fiber に移植したようなライブラリになっており、Three.js のアニメーションを宣言的に、シンプルに記述することができます。

前提知識として Framer Motion と React Three Fiber (以下 r3f ) の使い方を知っておく必要があることに注意してください。

検証環境

  • Node.js: v20.10.0
  • Next.js: v14.0.4
  • Three.js: v159
  • Framer Motion / Framer Motion 3D: v10.16.6
  • React Three Fiber: v8.15.12

セットアップ

今回は Next.js を使用しています。

# nextプロジェクト作成
npx create-next-app@latest
cd my-app
npm install

# 必要なライブラリのインストール
npm install three @react-three/fiber framer-motion-3d

基本的な使い方

Framer Motion と同じように、動きをつけたい r3f コンポーネントの先頭にmotionをつけて使用します。ジオメトリやメッシュ、ライト、マテリアルなど、全ての r3f コンポーネントにmotionが用意されています。

import { Canvas } from "@react-three/fiber";
import { motion } from "framer-motion-3d";

export default function MotionPage() {
  return (
    ...
    <motion.boxGeometry />

    <motion.meshNormalMaterial />

    <motion.pointLight />
    ...
  );
}

motion コンポーネントの props の中には animatevariants があり、animate には状態を渡し、variants には状態ごとの具体的なオブジェクトの動きを記述することで、状態に応じたアニメーションを作ることができます。

下のコードは、シーン内に球を用意し、球をクリックすると 1 秒かけて色が徐々に切り替わる実装です。

type Color = "pink" | "cyan";

export default function SwitchColorPage() {
  const [color, setColor] = useState<Color>("pink");

  const changeColor = () => {
    if (color == "pink") {
      setColor("cyan");
    } else {
      setColor("pink");
    }
  };

  return (
    <main className="w-full h-screen">
      <Canvas>
        <motion.mesh onClick={() => changeColor()}>
          <sphereGeometry />
          <motion.meshPhongMaterial
            animate={[color]}
            variants={{
              pink: { color: "#ff99dd" },
              cyan: { color: "#55ccff" },
            }}
            transition={{ duration: 1.0 }}
          />
        </motion.mesh>
        <pointLight position={[3, 2, 5]} intensity={50} />
        <ambientLight />
      </Canvas>
    </main>
  );
}

状態colorがキーとなり、状態が変更された時に対応するvariants内の値に遷移します。
transitionには、そのオブジェクト全体でのアニメーションの遷移に関する記述をします。この例ではアニメーションの長さとしてdurationに 1 秒を設定しています。
Transition に関する設定(Framer Motion)

座標や回転、色などのプロパティごとに遷移の仕方を変えたい場合は、animate内にtransitionプロパティを追加して設定します。

<motion.mesh
  animate={[isHover ? "hover" : ""]}
  variants={{
    hover: {
      rotateZ: 2 * Math.PI,
      transition: {
        rotateZ: { duration: 2, ease: "linear", repeat: Infinity },
      },
    },
  }}
>
  ...
</motion.mesh>

ホバー時のアニメーションを実装したいとき、ホバーしたオブジェクトそのものに設定したい場合はwhileHoverを使うことで実現できます。

<motion.mesh whileHover={{ scale: 2 }} rotation={[90, 0, 90]}>
  <boxGeometry />
  <meshNormalMaterial />
</motion.mesh>

ただし、別の motion コンポーネントがホバーしたのに合わせてアニメーションさせたい場合は、ホバーしているかどうかの状態を自分で作成し、onHoverStartonHoverEndで状態を切り替え、動かしたいコンポーネントのanimateにその状態を渡して使用するといった実装を行う必要があります。

作例

通常の Framer Motion と Framer Motion 3D を組み合わせて作成したアニメーションを 2 つ作ったため紹介します。

デモ&コード(CodeSandbox)

作例 ①: いいねボタン

ホバーやクリックに応じて立体的にハートが動いたり、色が変わります。
ハートのアニメーションの部分のみ 3D で、ボタンそのものやテキストは 2D 要素になります。

作例 ②: サイコロ

クリックするとサイコロが振られ、出た目が上のスコアに加算されます。
スコア部分は 2D 要素です。

さいごに

本記事では Framer Motion 3D の基本的な使い方や API の一部についてのみ触れました。Framer Motion 自体が豊富な機能を持っているので、3D ではより表現の幅が広がりそうだなと個人的に思います。
初投稿で拙い文章でしたが、読んでいただきありがとうございました。

続いて、フラー株式会社 Advent Calendar 24 日目は yoRyuuuuu さんの「急に PC が壊れることに備えよう」です!

Discussion