
サイコロゲームで比較!React Three Fiber vs Framer Motion
作成日:
更新日:
ブラウザで動くサイコロゲームを2つの異なるアプローチで実装してみました。1〜3個のサイコロを選んで振ることができます。
2つのアプローチ
| アプローチ | ライブラリ | 特徴 |
|---|---|---|
| 3D版 | React Three Fiber + drei | 本格的な3Dレンダリング、WebGL |
| 2D版 | Framer Motion + CSS | CSS 3D transformで疑似3D、軽量 |
どちらも同じ機能を持ちますが、実装方法と見た目、パフォーマンス特性が異なります。
React Three Fiber版(本格3D)
WebGLを使った本格的な3Dサイコロです。カメラをドラッグで回転、スクロールでズームできます。
3Dシーンを読み込み中...
特徴
- リアルな3D表現: RoundedBoxで角丸のサイコロを表現
- 物理的な質感: PBRマテリアルと環境マッピング
- インタラクティブ: カメラ操作(回転・ズーム)
- 影と光: ContactShadowsでリアルな接地影
Framer Motion版(CSS疑似3D)
CSSのtransform-style: preserve-3dを使った疑似3Dサイコロです。軽量でシンプルな実装です。
読み込み中...
特徴
- 軽量: WebGLを使わないため、低負荷
- シンプル: CSSとFramer Motionのみで実装
- スムーズ: Framer Motionのspringアニメーション
- 互換性: WebGL非対応環境でも動作
比較表
| 項目 | React Three Fiber | Framer Motion |
|---|---|---|
| 見た目 | 本格的な3D | 疑似3D(平面的) |
| パフォーマンス | やや重い(WebGL) | 軽量(CSS) |
| 実装難易度 | 中〜高 | 低〜中 |
| カメラ操作 | 可能 | 不可 |
| ブラウザ互換性 | WebGL必要 | ほぼ全て |
| ファイルサイズ | 大きい(Three.js) | 小さい |
| 用途 | 本格的な3D演出 | 軽い演出・UI |
実装解説
共通:サイコロロジック(useDice.ts)
両方のバージョンで共通のカスタムフックを使用しています。
export function useDice(initialCount: number = 1) {
const [count, setCount] = useState(initialCount);
const [values, setValues] = useState<number[]>([]);
const [isRolling, setIsRolling] = useState(false);
const roll = useCallback(() => {
setIsRolling(true);
// アニメーション中に値を何度か変更(演出)
const interval = setInterval(() => {
const tempValues = Array.from({ length: count }, () =>
Math.floor(Math.random() * 6) + 1
);
setValues(tempValues);
}, 100);
// 1秒後に確定
setTimeout(() => {
clearInterval(interval);
const finalValues = Array.from({ length: count }, () =>
Math.floor(Math.random() * 6) + 1
);
setValues(finalValues);
setIsRolling(false);
}, 1000);
}, [count]);
return { values, isRolling, roll, count, setCount };
}React Three Fiber版のポイント
RoundedBoxで角丸サイコロ
dreiのRoundedBoxを使うと、簡単に角丸の立方体を作成できます。
import { RoundedBox } from '@react-three/drei';
<RoundedBox args={[1, 1, 1]} radius={0.08} smoothness={4}>
<meshStandardMaterial color="#f5f5f5" roughness={0.3} metalness={0.1} />
</RoundedBox>useFrameで回転アニメーション
useFrameフックで毎フレームの更新処理を行います。
import { useFrame } from '@react-three/fiber';
useFrame((_, delta) => {
if (isRolling) {
// ロール中:高速回転
groupRef.current.rotation.x += rollSpeed.current.x * delta;
groupRef.current.rotation.y += rollSpeed.current.y * delta;
// 徐々に減速
rollSpeed.current.x *= 0.98;
rollSpeed.current.y *= 0.98;
} else {
// ロール終了:目標の向きにスムーズに回転
groupRef.current.rotation.x = THREE.MathUtils.lerp(
groupRef.current.rotation.x,
targetRotation.current.x,
delta * 5
);
}
});各面にドットを配置
サイコロの各面(1〜6)にドット(球体)を配置します。
// 各面のドット位置を計算
const getDotPositions = (face: string, value: number) => {
const offset = 0.51; // 面からの距離
const spread = 0.25; // ドット間の距離
// 値に応じたパターンを返す
// ...
};
// ドットを配置
{dots.map((pos, i) => (
<mesh key={i} position={pos}>
<sphereGeometry args={[0.08, 16, 16]} />
<meshStandardMaterial color="#1a1a1a" />
</mesh>
))}Framer Motion版のポイント
CSS 3D Transformで疑似3D
transform-style: preserve-3dとFramer MotionのrotateX/Y/Zを組み合わせます。
<motion.div
style={{
transformStyle: 'preserve-3d',
perspective: 1000,
}}
animate={
isRolling
? {
rotateX: [0, 360, 720, 1080],
rotateY: [0, 180, 360, 540],
rotateZ: [0, 90, 180, 270],
}
: { rotateX: 0, rotateY: 0, rotateZ: 0 }
}
transition={{
duration: 1,
ease: 'easeOut',
}}
>
{/* サイコロの目 */}
</motion.div>ドットのアニメーション
値が変わるたびにドットがポップインするアニメーションを追加。
{dots.map((dot, index) => (
<motion.div
key={index}
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{
delay: index * 0.05,
type: 'spring',
stiffness: 400,
}}
style={{
position: 'absolute',
left: `${dot.x}%`,
top: `${dot.y}%`,
borderRadius: '50%',
background: '#1a1a1a',
}}
/>
))}使い分けのガイドライン
React Three Fiberを選ぶべき場合
- 本格的な3D演出が必要な場合
- カメラ操作や物理演算を使いたい場合
- 環境マッピングやリアルな影が必要な場合
- 3Dモデルのインポートが必要な場合
Framer Motionを選ぶべき場合
- 軽量な演出で十分な場合
- パフォーマンス優先の場合
- WebGL非対応環境も考慮する場合
- シンプルな実装を求める場合
- UIのマイクロインタラクションとして使う場合
まとめ
同じ「サイコロを振る」機能でも、実装方法によって見た目や特性が大きく異なります。
| 観点 | R3F | Framer Motion |
|---|---|---|
| リッチな3D体験 | ◎ | △ |
| 軽量・シンプル | △ | ◎ |
| 学習コスト | やや高い | 低い |
プロジェクトの要件に応じて、適切なアプローチを選択しましょう。
ポイント:
- 両方ともReactのコンポーネントモデルを活かせる
- 状態管理は共通のカスタムフックで抽象化可能
- 見た目の違いは技術選択で決まる
ぜひ両方試して、違いを体感してみてください!