1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

虹色の背景で動く掲示板を作ってみた

Posted at

はじめに

初めましての方も、そうでない方もこんにちは!
気づけば残る単位はゼミだけとなり、友人と顔を合わせる機会もめっきり減ってしまいました。
そのせいか、ふと「最近ちょっと暇だなぁ」と思うことが増えてきました。

もちろん予定を合わせれば会うこともできますが、大学で一緒に講義を受けたり、課題に取り組んだりしていた時間も、今思えばとても楽しかったなと感じます。
だからこそ、残りの大学生活をより良いものにするために、この時間を大切に過ごしたいと思いました。

さて本題ですが、
最近、友人がシミュレーションツールを自作しているのを見て「はぇ〜、すごいな〜」と素直に感心しました。
説明を聞いても正直、半分くらいしか理解できなかったのですが、それでも「自分も何か作ってみたい!」という気持ちが湧いてきたんです。

そこで今回は、マウスに反応して反発しながら動く虹色のボールを背景にした掲示板を作ってみました!
もしよければ、最後まで読んでいただけると嬉しいです!

プロジェクト構成

最終的なフォルダ構成はこちらです:

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);
};

動かしてみよう

完成したものがこちら!

image.png

マウスを動かすと、それに反応してゼリー状の球がプルプルと反発します。
動きが滑らかで、見ているだけでも気持ちいいです!
また、「投稿する」ボタンを押すと、周囲の球が一気に弾けてとても爽快です。

おわりに

今回の記事はいかがだったでしょうか?
作っているうちに、「まだまだ工夫次第でもっと面白くできそうだな」と感じました。
皆さんもぜひ、自分なりのアレンジを加えて作ってみてください!

またどこかの記事でお会いしましょう!

GithubURL

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?