ブラウザで“ちょうどいい”抽選を|Webルーレットを作った話

はじめに

忘年会の景品抽選、配信企画の出演順、軽いチーム分け。
誰がやる?」と微妙な沈黙が落ちる、あの気まずい一瞬。ほんの少しだけ気を使う。けれど、本当はもっとライトに、もっと楽しく決めたい。

そんな小さな“当惑”を解消したくて、ブラウザだけで使えるミニWebアプリ「Webルーレット」を作った。

登録不要・その場で即回転。
この記事では、UI構成・Canvas描画・アニメーション・操作性のこだわりなど、実装の裏側をざっくり紹介する。
フロントエンドで小さめのツールを作ってみたい人の参考になればうれしい。


スポンサーリンク
スポンサーリンク

画面構成とUI設計

レイアウトはとてもシンプルに「左:ルーレット本体 / 右:設定パネル」。
イベントでプロジェクター投影しても破綻しにくい、安定した二分割だ。

ブラウザ幅900px以下では縦積みに変わり、スマホでも違和感なく操作できる。flexだけで完結する素直なレスポンシブ設計である。

<div class="wr-app"> … </div>
main.wr-main {
  display: flex;
  gap: 56px;
}
@media (max-width: 900px) {
  main.wr-main { flex-direction: column; }
}

左カラムにはCanvasで描くルーレットと結果パネル、右カラムには項目リスト・タイトル設定・速度スライダー。
背景色は淡いブルーグリーンで、カードはオフホワイト。視認性を損なわず、ほんのり“おもちゃ感”を出している。

特にルーレット枠は太いボーダー+内側シャドウで質感を足し、デジタルなのにどこかアナログっぽい“盤”に寄せた。ここは完全に趣味であるが、場のテンションを左右するため意外と重要だった。


ルーレット描画とセクター分割ロジック

ルーレットはHTMLCanvasに扇形を項目数ぶん描く、王道の実装。
角度は (2\pi / N) の等分割。文字も毎セクターの中心角に合わせて回転して描画する。

const sliceAngle = (Math.PI * 2) / items.length;

実装は以下のような流れ。

  • items配列に { label, color } を保持
  • 角度 angle を基準に扇形を順番に描画
  • テキストは ctx.rotate() で向きを合わせる
  • 真ん中に白円を敷いて盤の中心を演出
  • 星バッジ & ポインタは DOM で重ねて調整しやすくする

DOM重ね方式にしたのは、Canvas内にポインタを描くと当たり判定の座標計算が煩雑になるため。UI調整のしやすさを優先した。


当たり判定

Canvas自体を回転する方法もあるが、今回は盤は固定・ロジックだけ回転という方式にした。
常に「画面上部(星バッジ直下)」が当たりになるため、計算は次のように整理できる。

const pointerAngle = -Math.PI / 2; // 真上
diff = (pointerAngle - angle) を 0〜2π に正規化
index = floor(diff / sliceAngle)

ポイントは、angle がどれだけ蓄積しても周期 2π で正確にセクターを割り出せること。
UI的にも「星が固定・盤が回る」方が視覚的にわかりやすく、イベント中も説明いらず。

CSSで固定されたポインタと星バッジは以下のように配置している。

.wr-pointer {
  border-bottom: 22px solid #ff4b5c;
}

スタートボタンと減速アニメーション

回転は requestAnimationFrame ベースのアニメーション。
実装は素朴だが、挙動にはちょっとした“はじけ具合”を仕込んでいる。

  • 初期速度は range(1〜5) + 微ランダム を合成
  • 毎フレーム angularVelocity *= 0.985; で減速
  • 閾値を下回ったら停止 → 当選判定
  • UI文言(「回転中…」)で状態を明示
angularVelocity = base + add + Math.random() * 0.1;

速度にランダムを入れているのは、毎回同じ止まり方だと“機械感”が強くなるため
このほんの少しのゆらぎで「止まる瞬間」の盛り上がりが変わる。


項目リストUI

右パネルは、単なるテキストエリアにせず1行=1アイテムの構造で実装した。

  • 入力欄
  • カラードット(Canvasの色と連動)
  • 削除ボタン

この3点セットが揃うと、ルーレット上の色と項目の対応が一瞬で理解できる。
配信やイベント運営を想定すると、この“理解に要する1秒”が意外に重要だ。

削除処理は 最低2項目は残す 制約を入れ、ルーレットとして成立しない状態を防止。
行の高さも固定しており、入力中にUIが伸び縮みして“揺れる”ことがない。

初期値には

リンゴ・ゴリラ・ラッパ・パセリ

という、謎に語呂の良いセットを採用。動作確認しやすく、少しだけクスッとする。


タイトル & 速度スライダー

何を選ぶ抽選なのか、それが画面に書かれているだけで参加者の理解が段違いになる。
そのため「タイトル」欄を設け、結果は

忘年会の抽選会:◯◯

のように表示する。

また速度スライダーは、意外と好評だった機能。
速く回せば派手に、遅くすれば“じわじわ止まる”緊張感が出る。
ちょっとした演出の調整ができるだけで、場の空気が変わる。


まとめ

Webルーレットは、HTML / CSS / 素のJavaScript と Canvas だけの、非常に小さなWebアプリだ。
とはいえ、

  • 星バッジ基準の当たり判定
  • 減速アニメーションの微調整
  • 揺れない項目リストUI
  • “色と名前の即時リンク”

など、細部に少しずつ手をかけることで “ただのランダムツールではない体験” に近づけられた。

イベントの抽選、罰ゲーム決め、授業でのランダム指名──ブラウザさえあればどこでも回せる。

こうした小さなユーティリティを積み上げていくことが、フロントエンド開発のいちばんの筋トレだと思う。
次の小さなツールづくりのヒントになればうれしい。

コメント