Motion(旧 Framer Motion)アニメオーケストレーション設計:timeline・遅延・相互依存の整理術

複数のアニメーション要素を美しく連動させることは、モダンな Web アプリケーションにおいて重要な要素です。Motion(旧 Framer Motion)を使えば、複雑なアニメーションシーケンスを直感的に組み立てられますが、timeline 設計や遅延制御、要素間の依存関係を適切に管理しなければ、コードが複雑化してしまいます。
本記事では、Motion のアニメーションオーケストレーション機能を体系的に整理し、実務で使える設計パターンをご紹介します。
背景
Motion のオーケストレーション機能とは
Motion は、React 向けのアニメーションライブラリとして、単一要素のアニメーションだけでなく、複数の要素を連携させる「オーケストレーション機能」を提供しています。
この機能を使うことで、以下のような表現が可能になります。
- 複数の要素を順番にアニメーションさせる
- 親要素の動きに合わせて子要素を連動させる
- 特定の要素の完了を待って次の要素を動かす
- タイムライン上で複数のアニメーションを同期させる
以下の図は、Motion のオーケストレーション機能における基本的な制御の流れを示しています。
mermaidflowchart TB
parent["親コンポーネント<br/>(motion.div)"]
child1["子要素1<br/>(motion.div)"]
child2["子要素2<br/>(motion.div)"]
child3["子要素3<br/>(motion.div)"]
parent -->|"variants適用"| child1
parent -->|"variants適用"| child2
parent -->|"variants適用"| child3
child1 -->|"transition.delay"| child2
child2 -->|"transition.delay"| child3
style parent fill:#e1f5ff
style child1 fill:#fff4e1
style child2 fill:#fff4e1
style child3 fill:#fff4e1
上図のように、親要素から子要素へ variants を伝播させることで、統一されたアニメーション制御が実現できます。
オーケストレーションが必要とされる場面
実務では、以下のようなシーンでオーケストレーション設計が求められます。
- UI 要素の段階的表示: メニューやモーダルの開閉時に、各要素を順番に表示する
- ローディングアニメーション: 複数のスケルトンやプログレスバーを連動させる
- ページ遷移: コンテンツがフェードアウトしてから次のページがフェードインする
- データビジュアライゼーション: グラフや図表の各パーツを順番に描画する
- スクロール連動アニメーション: スクロール位置に応じて複数の要素を段階的に動かす
これらのシーンでは、単純なtransition
プロパティだけでは対応が難しく、体系的な設計アプローチが必要になります。
課題
アニメーション制御が複雑化する要因
Motion でアニメーションオーケストレーションを実装する際、以下のような課題に直面することがあります。
課題 1:遅延タイミングの管理が煩雑
複数の要素に異なる遅延時間を設定する場合、ハードコーディングされた数値が散在しがちです。
typescript// アンチパターン:遅延時間がハードコーディングされている
<motion.div transition={{ delay: 0.2 }} />
<motion.div transition={{ delay: 0.4 }} />
<motion.div transition={{ delay: 0.6 }} />
このような実装では、アニメーション全体のタイミングを調整する際に、すべての要素を手動で修正する必要があります。
課題 2:親子関係の依存が明示的でない
親要素のアニメーション完了を待ってから子要素を動かしたいケースで、制御フローが不明瞭になることがあります。
typescript// 親と子の依存関係が見えにくい
const parent = {
initial: { opacity: 0 },
animate: { opacity: 1, transition: { duration: 0.5 } },
};
const child = {
initial: { y: 20 },
animate: { y: 0, transition: { delay: 0.5 } }, // なぜ0.5秒?
};
上記のコードでは、子要素の遅延時間(0.5 秒)が親要素の継続時間と一致していることが、コメントなしでは理解しにくくなっています。
課題 3:複雑なタイムラインの可視化が困難
多数の要素が絡むアニメーションでは、全体のタイムラインを把握することが難しくなります。
以下の図は、アニメーションタイムラインが複雑化する様子を示しています。
mermaidsequenceDiagram
participant Header as ヘッダー
participant Nav as ナビゲーション
participant Content as コンテンツ
participant Footer as フッター
Note over Header,Footer: ページ読み込み開始
Header->>Header: フェードイン (0ms-300ms)
Note over Nav: Header完了後
Nav->>Nav: スライドイン (300ms-600ms)
Note over Content: Nav完了後
Content->>Content: フェードイン (600ms-900ms)
Note over Footer: Content完了後
Footer->>Footer: フェードイン (900ms-1200ms)
このような連鎖的なアニメーションは、コードだけでは全体像を把握しにくく、保守性が低下します。
課題 4:再利用可能なパターンの欠如
プロジェクト全体で統一されたアニメーションパターンがないと、各開発者が独自の実装をしてしまい、コードの一貫性が失われます。
これらの課題を解決するには、体系的なオーケストレーション設計パターンが必要です。
解決策
オーケストレーション設計の 3 つの柱
Motion のアニメーションオーケストレーションを整理するには、以下の 3 つの要素を体系的に管理します。
# | 要素 | 役割 | 主な機能 |
---|---|---|---|
1 | Variants | 状態定義とタイムライン制御 | staggerChildren 、delayChildren |
2 | Transition | 個別アニメーションのタイミング制御 | delay 、duration 、when |
3 | Orchestration | 要素間の依存関係管理 | onAnimationComplete 、カスタムフック |
これらを組み合わせることで、複雑なアニメーションを明瞭に設計できます。
設計パターン 1:Variants によるカスケード制御
Variants を使うと、親要素から子要素へアニメーション状態を伝播させられます。
基本的な Variants 定義
typescript// 親要素のvariants定義
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1, // 子要素を0.1秒ずつ遅延
delayChildren: 0.3, // 最初の子要素を0.3秒遅延
},
},
};
このコードでは、staggerChildren
で子要素を順番に表示し、delayChildren
で全体の開始タイミングを制御しています。
子要素の Variants 定義
typescript// 子要素のvariants定義
const itemVariants = {
hidden: {
opacity: 0,
y: 20,
},
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.5,
ease: 'easeOut',
},
},
};
子要素では、親から伝播されるhidden
とvisible
状態に対応するアニメーションを定義します。
コンポーネントへの適用
typescriptimport { motion } from 'motion/react';
export function StaggeredList() {
return (
<motion.ul
variants={containerVariants}
initial='hidden'
animate='visible'
>
{items.map((item) => (
<motion.li key={item.id} variants={itemVariants}>
{item.text}
</motion.li>
))}
</motion.ul>
);
}
このように、親要素にvariants
、initial
、animate
を設定するだけで、子要素に自動的にアニメーションが適用されます。
以下の図は、Variants によるカスケード制御の流れを示しています。
mermaidflowchart LR
container["motion.ul<br/>(containerVariants)"]
item1["motion.li[0]<br/>(itemVariants)"]
item2["motion.li[1]<br/>(itemVariants)"]
item3["motion.li[2]<br/>(itemVariants)"]
container -->|"delayChildren: 0.3s"| item1
item1 -->|"staggerChildren: 0.1s"| item2
item2 -->|"staggerChildren: 0.1s"| item3
note1["t=0.3s<br/>opacity: 0→1"]
note2["t=0.4s<br/>opacity: 0→1"]
note3["t=0.5s<br/>opacity: 0→1"]
item1 -.->|アニメーション| note1
item2 -.->|アニメーション| note2
item3 -.->|アニメーション| note3
上図のように、親要素の設定だけで子要素のタイミングが自動的に決定されるため、管理が容易になります。
設計パターン 2:Transition による詳細な制御
Variants だけでは不十分な場合、transition
プロパティで詳細な制御を行います。
複数プロパティの個別制御
typescript// 異なるプロパティに異なるタイミングを適用
const complexVariants = {
hidden: {
opacity: 0,
scale: 0.8,
y: 50,
},
visible: {
opacity: 1,
scale: 1,
y: 0,
transition: {
// opacityとscaleを先に完了させる
opacity: { duration: 0.3 },
scale: { duration: 0.3 },
// yは遅れて開始し、長めに実行
y: { delay: 0.2, duration: 0.6, ease: 'easeOut' },
},
},
};
このコードでは、フェードイン・スケールアップを先に完了させてから、スライドアニメーションを実行しています。
when オプションによる順序制御
typescript// アニメーション完了を待ってから次を実行
const sequentialVariants = {
hidden: { opacity: 0, x: -100 },
visible: {
opacity: 1,
x: 0,
transition: {
opacity: { duration: 0.3 },
// opacityが完了してからxを実行
x: { duration: 0.5 },
when: 'beforeChildren', // 子要素のアニメーション前に完了
},
},
};
when
オプションを使うことで、親子間のアニメーション順序を明示的に制御できます。
利用可能な when オプション
# | オプション | 動作 | 使用場面 |
---|---|---|---|
1 | beforeChildren | 親が完了してから子を開始 | モーダルの背景 → コンテンツ |
2 | afterChildren | 子が完了してから親を開始 | 子要素の読み込み → 全体表示 |
3 | 未指定(デフォルト) | 親と子が同時に開始 | 通常のカスケード表示 |
設計パターン 3:相互依存の管理
複数の独立した要素が相互に依存する場合、状態管理とコールバックを組み合わせます。
状態管理による連鎖制御
typescriptimport { useState } from "react";
import { motion } from "motion/react";
export function SequentialAnimation() {
const [step, setStep] = useState(0);
return (
<>
{/* ステップ1 */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: step >= 0 ? 1 : 0 }}
onAnimationComplete={() => setStep(1)}
>
最初の要素
</motion.div>
この実装では、状態変数step
によってアニメーションの進行を管理しています。
コールバックによる次ステップのトリガー
typescript {/* ステップ2 */}
<motion.div
initial={{ x: -50, opacity: 0 }}
animate={{
x: step >= 1 ? 0 : -50,
opacity: step >= 1 ? 1 : 0
}}
onAnimationComplete={() => setStep(2)}
>
次の要素
</motion.div>
{/* ステップ3 */}
<motion.div
initial={{ y: 20, opacity: 0 }}
animate={{
y: step >= 2 ? 0 : 20,
opacity: step >= 2 ? 1 : 0
}}
>
最後の要素
</motion.div>
</>
);
}
onAnimationComplete
コールバックで次のステップをトリガーすることで、厳密な順序制御が可能になります。
カスタムフックによる再利用可能な設計
typescript// アニメーションステップを管理するカスタムフック
import { useState, useCallback } from 'react';
export function useAnimationSequence(totalSteps: number) {
const [currentStep, setCurrentStep] = useState(0);
const nextStep = useCallback(() => {
setCurrentStep((prev) =>
prev < totalSteps - 1 ? prev + 1 : prev
);
}, [totalSteps]);
const isStepActive = useCallback(
(step: number) => {
return currentStep >= step;
},
[currentStep]
);
return { currentStep, nextStep, isStepActive };
}
このフックは、アニメーションシーケンス全体のステップ管理を抽象化しています。
カスタムフックの利用例
typescriptexport function ImprovedSequentialAnimation() {
const { nextStep, isStepActive } =
useAnimationSequence(3);
return (
<>
<motion.div
animate={{ opacity: isStepActive(0) ? 1 : 0 }}
onAnimationComplete={nextStep}
>
最初の要素
</motion.div>
<motion.div
animate={{ opacity: isStepActive(1) ? 1 : 0 }}
onAnimationComplete={nextStep}
>
次の要素
</motion.div>
<motion.div
animate={{ opacity: isStepActive(2) ? 1 : 0 }}
>
最後の要素
</motion.div>
</>
);
}
カスタムフックを使うことで、コードの可読性と再利用性が大幅に向上します。
具体例
実例 1:モーダルの段階的表示
モーダルを開く際に、背景オーバーレイ → コンテナ → 各要素の順で表示する実装です。
型定義とインターフェース
typescript// モーダルのプロパティ定義
interface ModalProps {
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
}
interface ModalContentItem {
id: string;
content: React.ReactNode;
}
型定義によって、コンポーネントの仕様が明確になります。
Variants 定義
typescript// 背景オーバーレイのvariant
const overlayVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { duration: 0.2 },
},
};
// モーダルコンテナのvariant
const modalVariants = {
hidden: {
opacity: 0,
scale: 0.8,
y: 50,
},
visible: {
opacity: 1,
scale: 1,
y: 0,
transition: {
duration: 0.3,
delay: 0.1, // オーバーレイの後に開始
when: 'beforeChildren', // 子要素の前に完了
},
},
};
各レイヤーのアニメーションタイミングを、delay
とwhen
で明確に定義しています。
コンテンツ要素の Variants
typescript// モーダル内コンテンツのvariant
const contentVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: {
staggerChildren: 0.08, // 各要素を0.08秒ずつ遅延
delayChildren: 0.1, // 全体を0.1秒遅延
},
},
};
const itemVariants = {
hidden: { opacity: 0, x: -20 },
visible: {
opacity: 1,
x: 0,
transition: { duration: 0.4 },
},
};
staggerChildren
によって、コンテンツが滑らかに順番に表示されます。
Modal コンポーネントの実装
typescriptimport { motion, AnimatePresence } from "motion/react";
export function Modal({ isOpen, onClose, children }: ModalProps) {
return (
<AnimatePresence>
{isOpen && (
<>
{/* 背景オーバーレイ */}
<motion.div
variants={overlayVariants}
initial="hidden"
animate="visible"
exit="hidden"
onClick={onClose}
style={{
position: "fixed",
inset: 0,
backgroundColor: "rgba(0, 0, 0, 0.5)"
}}
/>
AnimatePresence
を使うことで、モーダルの開閉時に自動的にアニメーションが適用されます。
モーダルコンテンツの実装
typescript {/* モーダルコンテナ */}
<motion.div
variants={modalVariants}
initial="hidden"
animate="visible"
exit="hidden"
style={{
position: "fixed",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: "white",
padding: "2rem",
borderRadius: "8px"
}}
>
{/* コンテンツリスト */}
<motion.div variants={contentVariants}>
{React.Children.map(children, (child, index) => (
<motion.div key={index} variants={itemVariants}>
{child}
</motion.div>
))}
</motion.div>
</motion.div>
</>
)}
</AnimatePresence>
);
}
この実装により、モーダルの開閉時に美しい段階的アニメーションが実現できます。
以下の図は、モーダル表示のアニメーションタイムラインを示しています。
mermaidsequenceDiagram
participant User as ユーザー操作
participant Overlay as 背景オーバーレイ
participant Container as モーダルコンテナ
participant Content as コンテンツ要素
User->>Overlay: モーダル表示クリック
Note over Overlay: 0ms - 200ms
Overlay->>Overlay: フェードイン (opacity: 0→1)
Note over Container: 100ms - 400ms
Container->>Container: スケール&スライド<br/>(scale: 0.8→1, y: 50→0)
Note over Content: 500ms -
Container->>Content: 子要素アニメーション開始
loop 各コンテンツ要素(0.08秒ずつ)
Content->>Content: スライドイン (x: -20→0)
end
このタイムラインにより、ユーザーは視覚的な階層を認識しながらモーダルを理解できます。
実例 2:データ読み込みのローディングシーケンス
API からデータを取得する際の、段階的なローディング表示の実装です。
データ取得状態の型定義
typescript// データ取得の状態を表す型
type LoadingState =
| 'idle'
| 'loading'
| 'success'
| 'error';
interface DataLoadingProps {
state: LoadingState;
progress?: number; // 0-100
data?: any;
}
明確な型定義により、各状態での挙動が明瞭になります。
ローディングアニメーションの Variants
typescript// スピナーのvariant
const spinnerVariants = {
loading: {
rotate: 360,
transition: {
duration: 1,
repeat: Infinity,
ease: 'linear',
},
},
};
// プログレスバーのvariant
const progressVariants = {
loading: {
scaleX: 1,
transition: {
duration: 2,
ease: 'easeInOut',
},
},
};
無限ループと進行状況を、それぞれ異なるアニメーションで表現しています。
ローディングコンポーネントの実装
typescriptexport function DataLoading({ state, progress = 0, data }: DataLoadingProps) {
return (
<AnimatePresence mode="wait">
{/* ローディング状態 */}
{state === "loading" && (
<motion.div
key="loading"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<motion.div
variants={spinnerVariants}
animate="loading"
style={{
width: "40px",
height: "40px",
border: "4px solid #e0e0e0",
borderTopColor: "#3b82f6",
borderRadius: "50%"
}}
/>
AnimatePresence
のmode="wait"
により、状態遷移時に前の要素が消えてから次の要素が表示されます。
プログレスバーとデータ表示
typescript <motion.div
initial={{ scaleX: 0 }}
animate={{ scaleX: progress / 100 }}
style={{
width: "200px",
height: "4px",
backgroundColor: "#3b82f6",
marginTop: "1rem",
transformOrigin: "left"
}}
/>
</motion.div>
)}
{/* 成功状態 */}
{state === "success" && (
<motion.div
key="success"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.4 }}
>
{data && <DataDisplay data={data} />}
</motion.div>
)}
</AnimatePresence>
);
}
各状態に対応したアニメーションを定義することで、ユーザーにわかりやすいフィードバックを提供できます。
実例 3:複雑なタイムライン制御
複数のセクションが連鎖的にアニメーションする、ランディングページの実装です。
セクション構成の定義
typescript// 各セクションの構成を定義
interface Section {
id: string;
title: string;
content: string;
delay: number; // 前のセクションからの遅延時間
}
const sections: Section[] = [
{
id: 'hero',
title: 'ヒーロー',
content: '...',
delay: 0,
},
{
id: 'features',
title: '機能',
content: '...',
delay: 0.3,
},
{
id: 'pricing',
title: '料金',
content: '...',
delay: 0.5,
},
{ id: 'cta', title: 'CTA', content: '...', delay: 0.4 },
];
データ駆動でセクションを定義することで、タイムラインの調整が容易になります。
タイムライン制御フックの実装
typescriptimport { useState, useEffect } from 'react';
export function useTimelineControl(sections: Section[]) {
const [activeSections, setActiveSections] = useState<
Set<string>
>(new Set());
useEffect(() => {
let cumulativeDelay = 0;
sections.forEach((section, index) => {
cumulativeDelay += section.delay * 1000;
setTimeout(() => {
setActiveSections(
(prev) => new Set([...prev, section.id])
);
}, cumulativeDelay);
});
}, [sections]);
const isSectionActive = (sectionId: string) => {
return activeSections.has(sectionId);
};
return { isSectionActive };
}
累積遅延時間を計算することで、各セクションの表示タイミングを正確に制御しています。
セクションコンポーネントの実装
typescript// 個別セクションのvariant
const sectionVariants = {
hidden: {
opacity: 0,
y: 60,
scale: 0.95,
},
visible: {
opacity: 1,
y: 0,
scale: 1,
transition: {
duration: 0.6,
ease: [0.22, 1, 0.36, 1], // カスタムイージング
},
},
};
interface SectionProps {
section: Section;
isActive: boolean;
}
function AnimatedSection({
section,
isActive,
}: SectionProps) {
return (
<motion.section
variants={sectionVariants}
initial='hidden'
animate={isActive ? 'visible' : 'hidden'}
>
<h2>{section.title}</h2>
<p>{section.content}</p>
</motion.section>
);
}
カスタムイージング関数を使うことで、より自然なアニメーションが実現できます。
ランディングページの実装
typescriptexport function LandingPage() {
const { isSectionActive } = useTimelineControl(sections);
return (
<main>
{sections.map((section) => (
<AnimatedSection
key={section.id}
section={section}
isActive={isSectionActive(section.id)}
/>
))}
</main>
);
}
このシンプルな実装で、複雑なタイムライン制御を実現できます。
以下の図は、ランディングページの各セクションのアニメーションタイミングを示しています。
mermaidflowchart TB
start["ページ読み込み"]
hero["ヒーローセクション<br/>(delay: 0s)"]
features["機能セクション<br/>(delay: 0.3s)"]
pricing["料金セクション<br/>(delay: 0.5s)"]
cta["CTAセクション<br/>(delay: 0.4s)"]
start -->|"0.0s"| hero
hero -->|"+0.3s = 0.3s"| features
features -->|"+0.5s = 0.8s"| pricing
pricing -->|"+0.4s = 1.2s"| cta
style start fill:#e1f5ff
style hero fill:#d4edda
style features fill:#d4edda
style pricing fill:#d4edda
style cta fill:#d4edda
上図のように、各セクションの累積遅延時間が視覚的に把握できることで、タイムライン全体の調整が容易になります。
図で理解できる要点
本章では、以下の 3 つの実例を通じて、オーケストレーション設計を実践的に学びました。
- モーダルは背景 → コンテナ → 要素の 3 層構造でアニメーションを設計する
- データローディングは状態ごとに異なるアニメーションパターンを適用する
- 複雑なタイムラインは累積遅延時間を計算して管理する
まとめ
Motion のアニメーションオーケストレーション設計には、以下の 3 つの要素が重要です。
設計の 3 つの柱を活用する
Variantsによって状態とタイムラインを定義し、Transitionで詳細なタイミングを制御し、カスタムフックで相互依存を管理することで、複雑なアニメーションを明瞭に実装できます。
データ駆動でタイムラインを設計する
遅延時間やアニメーション設定をハードコーディングせず、配列やオブジェクトとして定義することで、保守性と可読性が向上します。
段階的な制御パターンを使い分ける
単純なカスケード表示にはstaggerChildren
を、厳密な順序制御には状態管理を、複雑なタイムラインにはカスタムフックを使うことで、適切な設計が可能になります。
これらの設計パターンを活用することで、美しく保守しやすいアニメーションオーケストレーションを実現できるでしょう。
関連リンク
- article
Motion(旧 Framer Motion)アニメオーケストレーション設計:timeline・遅延・相互依存の整理術
- article
Motion(旧 Framer Motion)useAnimate/useMotionValueEvent 速習チートシート
- article
Motion(旧 Framer Motion)× Vite/Next/Turbopack:ツリーシェイク最適化とバンドル最小化の初期設定
- article
Motion(旧 Framer Motion)でカクつき・ちらつきを潰す:レイアウトシフトと FLIP の落とし穴
- article
Motion(旧 Framer Motion)アーキテクチャ概説:Renderer と Animation Engine を俯瞰する
- article
Motion(旧 Framer Motion)Gesture アニメーション実践:whileHover/whileTap/whileFocus の設計術
- article
NestJS クリーンアーキテクチャ:UseCase/Domain/Adapter を疎結合に保つ設計術
- article
WebSocket プロトコル設計:バージョン交渉・機能フラグ・後方互換のパターン
- article
MySQL 読み書き分離設計:ProxySQL で一貫性とスループットを両立
- article
Motion(旧 Framer Motion)アニメオーケストレーション設計:timeline・遅延・相互依存の整理術
- article
WebRTC で遠隔支援:画面注釈・ポインタ共有・低遅延音声の実装事例
- article
JavaScript パフォーマンス最適化大全:レイアウトスラッシングを潰す実践テク
- blog
iPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
- blog
Googleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
- blog
【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
- blog
Googleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
- blog
Pixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
- blog
フロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来