See the Pen JavaScript Class Animation Diagram by MONO365 -Color your days- (@monoqlo365) on CodePen.
import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
// Default export: interactive React component that visualizes how JS classes work
// - Uses Tailwind CSS for styling (assumed available in host)
// - Uses framer-motion for animations
// - Shows steps: Class declaration, constructor, instance creation, prototype chain, inheritance, super(), static, private fields
export default function ClassAnimation() {
const [step, setStep] = useState(0);
const steps = [
{
id: 0,
title: "クラスの宣言 (Class declaration)",
desc: "class Color { constructor(r,g,b) { ... } } のようにクラスは“設計図”として定義されます。",
},
{
id: 1,
title: "コンストラクタとインスタンス (constructor & instance)",
desc: "new Color(10,20,30) によりインスタンスが生成され、constructor 内の初期化処理が走ります。",
},
{
id: 2,
title: "プロトタイプチェーン (prototype chain)",
desc: "インスタンスはクラスの prototype を参照し、メソッドは prototype 上で共有されます。",
},
{
id: 3,
title: "継承 (extends / super)",
desc: "class ColorWithAlpha extends Color は親の機能を引き継ぎ、super() で親の constructor を呼べます。",
},
{
id: 4,
title: "静的メソッド / プライベートフィールド (static / #private)",
desc: "static はクラス自体に属し、# はプライベートで外部から参照できません。",
},
];
const next = () => setStep((s) => Math.min(s + 1, steps.length - 1));
const prev = () => setStep((s) => Math.max(s - 1, 0));
return (
<div className="p-6 max-w-5xl mx-auto">
<h1 className="text-2xl font-bold mb-4">クラスの内部をアニメーションで理解する</h1>
<p className="mb-4 text-sm text-gray-600">
ステップを進めると、クラス定義 → インスタンス生成 → プロトタイプチェーン → 継承 の流れが
アニメーションで表示されます。
</p>
<div className="flex gap-4 mb-6">
<div className="flex-1 bg-white rounded-2xl shadow p-4">
<h2 className="font-semibold">ステップ</h2>
<div className="mt-3">
{steps.map((s) => (
<button
key={s.id}
onClick={() => setStep(s.id)}
className={`block w-full text-left px-3 py-2 my-1 rounded ${
s.id === step ? "bg-sky-100 border-l-4 border-sky-500" : "hover:bg-gray-50"
}`}
>
<div className="text-sm font-medium">{s.title}</div>
<div className="text-xs text-gray-500">{s.desc}</div>
</button>
))}
</div>
<div className="mt-4 flex gap-2">
<button onClick={prev} className="px-3 py-2 rounded bg-gray-100">戻る</button>
<button onClick={next} className="px-3 py-2 rounded bg-sky-500 text-white">進む</button>
<div className="ml-auto text-xs text-gray-500 self-center">Step {step + 1}/{steps.length}</div>
</div>
</div>
<div className="flex-1 bg-white rounded-2xl shadow p-4">
<h2 className="font-semibold">解説</h2>
<div className="mt-3 text-sm text-gray-700">
<strong>{steps[step].title}</strong>
<p className="mt-2">{steps[step].desc}</p>
{/* show code snippets per step */}
<div className="mt-3">
<CodeSnippet step={step} />
</div>
</div>
</div>
</div>
{/* visualization area */}
<div className="bg-white rounded-2xl shadow p-6 h-96 relative overflow-hidden">
<AnimatePresence mode="wait">
{step === 0 && (
<motion.div
key="s0"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.4 }}
className="h-full flex items-center justify-center"
>
<div className="flex gap-6 items-center">
<Card title="class Color" subtitle="設計図(関数オブジェクト)">
<pre className="text-xs">constructor(r,g,b) {'{'} this.r = r; this.g = g; this.b = b; {'}'}
</pre>
</Card>
<Arrow right />
<div className="text-sm text-gray-600">クラス宣言は内部的に「関数オブジェクト」として作られる</div>
</div>
</motion.div>
)}
{step === 1 && (
<motion.div
key="s1"
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.4 }}
className="h-full flex items-center justify-center"
>
<div className="flex items-center gap-8">
<Card title="class Color" subtitle="設計図">
<pre className="text-xs">constructor(r,g,b) {'{'} this.r = r; this.g = g; this.b = b; {'}'}
</pre>
</Card>
<motion.div
initial={{ x: -100, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ duration: 0.6 }}
>
<Arrow right />
</motion.div>
<motion.div
initial={{ scale: 0.8, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.6 }}
>
<Card title="instance: c" subtitle="new Color(10,20,30)">
<pre className="text-xs">{`{ r:10, g:20, b:30 }`}</pre>
</Card>
</motion.div>
</div>
</motion.div>
)}
{step === 2 && (
<motion.div
key="s2"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="h-full flex items-center justify-center"
>
<div className="flex gap-8 items-start">
<div>
<Card title="instance: c" subtitle="{ r:10, g:20, b:30 }" small>
<pre className="text-xs">(own properties)</pre>
</Card>
<div className="mt-6 flex items-center justify-center">
<Arrow down />
</div>
</div>
<div className="flex flex-col items-center">
<Card title="Color.prototype" subtitle="メソッドがここにある">
<pre className="text-xs">getHex() {'{'} ... {'}'}</pre>
</Card>
<div className="mt-6 flex items-center justify-center">
<Arrow down />
</div>
<Card title="Object.prototype" subtitle="最上位のプロトタイプ">...</Card>
</div>
<div className="ml-6 text-sm text-gray-600 max-w-xs">
<p>プロパティ参照時、まずインスタンス自身を見て、なければ prototype を上へ辿ります。これがプロトタイプチェーンです。</p>
</div>
</div>
</motion.div>
)}
{step === 3 && (
<motion.div
key="s3"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="h-full flex items-center justify-center"
>
<div className="flex gap-8 items-center">
<div>
<Card title="class Color" subtitle="親クラス" small>
<pre className="text-xs">constructor(r,g,b){...}</pre>
</Card>
</div>
<div>
<Arrow right />
</div>
<div>
<Card title="class ColorWithAlpha extends Color" subtitle="子クラス">
<pre className="text-xs">constructor(r,g,b,a){ super(r,g,b); this.a=a; }</pre>
</Card>
</div>
</div>
</motion.div>
)}
{step === 4 && (
<motion.div
key="s4"
initial={{ opacity: 0, scale: 0.98 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0 }}
className="h-full flex items-center justify-center"
>
<div className="flex gap-8 items-center">
<Card title="Color" subtitle="静的メソッド & プライベート">
<pre className="text-xs">static isValid(){...}\n#values = [r,g,b]</pre>
</Card>
<div className="ml-4 text-sm text-gray-600 max-w-md">
<p>- <strong>static</strong> はクラス自体に紐づく。<br />- <strong>#</strong> はプライベートで外から参照できない。</p>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
<div className="mt-6 text-sm text-gray-500">ヒント: 各ステップのコードをよく読んで、実際にブラウザのコンソールで試してみましょう。</div>
</div>
);
}
// Small helper components used in the visualization
function Card({ title, subtitle, children, small }) {
return (
<div className={`rounded-xl p-4 shadow-md ${small ? "w-56" : "w-72"} bg-white border`}>
<div className="text-sm font-medium">{title}</div>
<div className="text-xs text-gray-500">{subtitle}</div>
<div className="mt-2 text-xs text-slate-700 whitespace-pre-wrap">{children}</div>
</div>
);
}
function Arrow({ right, down }) {
if (right) {
return (
<div className="flex items-center">
<svg width="60" height="24" viewBox="0 0 60 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 12 H48" stroke="#94a3b8" strokeWidth="2" strokeLinecap="round" />
<path d="M40 6 L48 12 L40 18" stroke="#94a3b8" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
);
}
if (down) {
return (
<div className="flex items-center">
<svg width="24" height="60" viewBox="0 0 24 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2 V48" stroke="#94a3b8" strokeWidth="2" strokeLinecap="round" />
<path d="M6 40 L12 48 L18 40" stroke="#94a3b8" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
);
}
return null;
}
HTMLエディタにプレビューできる React コンポーネント(JS Classes — アニメーション図解)を用意してあります。
左のステップ一覧で切り替えながら、クラス宣言 → インスタンス生成 → プロトタイプチェーン → 継承 → static/private をアニメーションで確認できます。
