はじめに
初めましての方も、そうでない方もこんにちは!
気づけば残る単位はゼミだけとなり、友人と顔を合わせる機会もめっきり減ってしまいました。
そのせいか、ふと「最近ちょっと暇だなぁ」と思うことが増えてきました。
もちろん予定を合わせれば会うこともできますが、大学で一緒に講義を受けたり、課題に取り組んだりしていた時間も、今思えばとても楽しかったなと感じます。
だからこそ、残りの大学生活をより良いものにするために、この時間を大切に過ごしたいと思いました。
さて本題ですが、
最近、友人がシミュレーションツールを自作しているのを見て「はぇ〜、すごいな〜」と素直に感心しました。
説明を聞いても正直、半分くらいしか理解できなかったのですが、それでも「自分も何か作ってみたい!」という気持ちが湧いてきたんです。
そこで今回は、マウスに反応して反発しながら動く虹色のボールを背景にした掲示板を作ってみました!
もしよければ、最後まで読んでいただけると嬉しいです!
プロジェクト構成
最終的なフォルダ構成はこちらです:
tama/
├── app/
│ ├── page.tsx # メインページ
│ ├── layout.tsx # レイアウト
│ └── globals.css # グローバルスタイル
├── components/ # UIコンポーネント
│ ├── RainbowCanvas.tsx # Canvas背景
│ └── BulletinBoard/ # 掲示板関連
│ ├── index.tsx # メインコンポーネント
│ ├── PostForm.tsx # 投稿フォーム
│ ├── PostList.tsx # 投稿リスト
│ └── PostItem.tsx # 個別投稿
├── hooks/ # カスタムフック
│ ├── useCanvas.ts # Canvas物理演算
│ ├── useBurst.ts # 弾ける効果
│ └── usePosts.ts # 投稿管理
├── lib/ # ビジネスロジック
│ ├── RigidBall.ts # 剛体球クラス
│ └── SpatialGrid.ts # 空間分割グリッド
├── types/ # 型定義
│ └── index.ts
└── constants/ # 定数
├── physics.ts # 物理定数
└── colors.ts # 色定数
使用技術
フレームワーク・言語
- Next.js
- TypeScript
物理演算の実装
Canvas API
ブラウザ標準の Canvas API を使用して描画しています。
const step = () => {
const mouseVelocity = updateMouseVelocity();
updateBalls(mouseVelocity);
resolveCollisions();
renderFrame();
animationFrameRef.current = requestAnimationFrame(step);
};
剛体物理演算
各ボールは次のような物理特性を持っています。
- 速度減衰:空気抵抗を表現
- 反発係数:壁や他のボールとの衝突を再現
- 静止判定:動かないボールは計算をスキップして最適化
class RigidBall {
vx: number = 0; // X方向の速度
vy: number = 0; // Y方向の速度
isResting: boolean = false; // 静止状態
applyForces(mouseX, mouseY, mouseVelocity, width, height) {
// 静止中&マウスが遠い場合はスキップ
if (this.isResting && !mouseNearby) return;
// 減衰を適用
this.vx *= DAMPING;
this.vy *= DAMPING;
// 位置を更新
this.x += this.vx;
this.y += this.vy;
}
}
演出
マウスからの反発
マウスの位置と速度をもとに、近くのボールに力を加えます。
const velocityBonus = Math.min(
1.0 + mouseVelocity * MOUSE_VELOCITY_MULTIPLIER,
MAX_MOUSE_VELOCITY_BONUS
);
const force = ratio * ratio * MOUSE_FORCE * velocityBonus;
入力時の弾ける効果
フォームをクリックすると、その中心座標にマウスがあるように見せかけて反発力を与えます。
const triggerBurst = (element: HTMLElement) => {
const rect = element.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
mouseRef.current.x = centerX;
mouseRef.current.y = centerY;
mouseVelocityRef.current = 15;
setTimeout(() => {
mouseRef.current.x = null;
mouseRef.current.y = null;
}, 300);
};
動かしてみよう
完成したものがこちら!
マウスを動かすと、それに反応してゼリー状の球がプルプルと反発します。
動きが滑らかで、見ているだけでも気持ちいいです!
また、「投稿する」ボタンを押すと、周囲の球が一気に弾けてとても爽快です。
おわりに
今回の記事はいかがだったでしょうか?
作っているうちに、「まだまだ工夫次第でもっと面白くできそうだな」と感じました。
皆さんもぜひ、自分なりのアレンジを加えて作ってみてください!
またどこかの記事でお会いしましょう!
GithubURL