参加者を決めるルーレット

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

何を作ったか

はじめに、何を作ったのかを書きます。

配信の企画で「誰がやるか」を決めるときに使う、参加者を決めるルーレットを作りました。名前をカンマで並べて書いておいて、ボタンを1つ押すと、その中の1つが大きく出ます。HTML のファイルが1枚と、その中に入れた JavaScript だけで動きます。外から部品を取ってくる書き方をしていませんので、開くときに何かが届いていないと動かない、という心配はいりません。

作った理由は、「次は誰の番か」を決める場面が、配信の企画にはよく出てくるからです。話し合いで決めると、決まらないまま時間が過ぎます。じゃんけんだと、画面の向こうにいる人には見えません。そこで、名前を入れておいてボタンを押すだけのものを用意しました。

同じことは、くじを引く、サイコロを振る、チャットの書き込みを数える、といったやり方でも決められます。それでも、名前をその場で足したり減らしたりできること、当たった人の名前が大きく出て、画面の向こうの人にも読めること、この2つは助かります。そこを狙って作りました。

中身は、見た目を決めるスタイル、名前を入れる欄、抽選するボタン、当たった人を出す欄、そして抽選の動きを書いた JavaScript です。書き上げた HTML は要素が21個で、閉じ忘れはありません。表示の確認は、OBS のブラウザソースに読み込んで行いました。

置くコード

作ったものをそのまま置きます。1枚のファイルです。名前を付けて保存し、OBS のブラウザソースに読み込ませると動きます。

<!DOCTYPE html>
<html lang='ja'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>参加者を決めるルーレット</title>
<style>
  :root {
    --card: #14181f;
    --line: #39424f;
    --text: #f3f6fa;
    --muted: #a8b3c1;
    --accent: #ffd166;
    --accent-dark: #2b2205;
  }
  * { box-sizing: border-box; }
  body {
    margin: 0;
    padding: 32px 16px 48px;
    background: #0a0d11;
    color: var(--text);
    font-family: 'Hiragino Kaku Gothic ProN', 'Yu Gothic', 'Noto Sans JP', 'Meiryo', sans-serif;
    line-height: 1.8;
    -webkit-font-smoothing: antialiased;
  }
  .card {
    max-width: 760px;
    margin: 0 auto;
    background: var(--card);
    border: 2px solid var(--line);
    border-radius: 18px;
    padding: 36px 32px 40px;
  }
  h1 {
    margin: 0 0 10px;
    font-size: 26px;
    line-height: 1.4;
    letter-spacing: 0.02em;
  }
  .lead {
    margin: 0 0 28px;
    font-size: 15px;
    line-height: 1.8;
    color: var(--muted);
  }
  .field-label {
    display: block;
    margin-bottom: 10px;
    font-size: 16px;
    color: var(--muted);
  }
  textarea {
    display: block;
    width: 100%;
    min-height: 132px;
    padding: 14px 16px;
    background: #0a0d11;
    color: var(--text);
    border: 2px solid var(--line);
    border-radius: 12px;
    font-family: inherit;
    font-size: 18px;
    line-height: 1.8;
    resize: vertical;
  }
  textarea:focus {
    outline: none;
    border-color: var(--accent);
  }
  .row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    margin-top: 18px;
    flex-wrap: wrap;
  }
  .count {
    margin: 0;
    font-size: 16px;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
  }
  .count strong {
    margin: 0 4px;
    font-size: 24px;
    font-weight: 700;
    color: var(--text);
  }
  button {
    flex: 1 1 240px;
    padding: 18px 32px;
    background: var(--accent);
    color: var(--accent-dark);
    border: none;
    border-radius: 12px;
    font-family: inherit;
    font-size: 22px;
    font-weight: 700;
    letter-spacing: 0.06em;
    cursor: pointer;
  }
  button:hover:not(:disabled) { filter: brightness(1.08); }
  button:disabled { opacity: 0.6; cursor: default; }
  .result-box {
    margin-top: 32px;
    padding: 28px 24px 32px;
    background: #0a0d11;
    border: 2px solid var(--line);
    border-radius: 14px;
    text-align: center;
  }
  .result-label {
    margin: 0 0 14px;
    font-size: 16px;
    color: var(--muted);
  }
  .result-name {
    margin: 0;
    min-height: 1.2em;
    font-size: clamp(40px, 9vw, 76px);
    font-weight: 700;
    line-height: 1.2;
    color: var(--accent);
    word-break: break-word;
    font-variant-numeric: tabular-nums;
  }
  .result-name.is-message {
    font-size: clamp(18px, 4vw, 24px);
    font-weight: 400;
    line-height: 1.6;
    color: var(--muted);
  }
</style>
</head>
<body>
  <main class='card'>
    <h1>参加者を決めるルーレット</h1>
    <p class='lead'>名前をカンマで区切って並べてください。ボタンを押すと、その中から1人が選ばれます。</p>

<label class='field-label' for='names'>参加者の名前(カンマ区切りで入力)</label><textarea id='names' rows='5'>あおい, かえで, さくら, つばさ, なお</textarea>

<div class='row'><p class='count' id='count'>参加者の人数:<strong>0</strong>名</p><button id='draw' type='button'>抽選する</button></div>

<div class='result-box'><p class='result-label'>選ばれた人:</p><p class='result-name is-message' id='result'>まだ抽選していません。ボタンを押してください。</p></div></main>

<script>(function () {var namesField = document.getElementById('names');var countText = document.getElementById('count');var drawButton = document.getElementById('draw');var resultText = document.getElementById('result');

function readNames() {return namesField.value.split(/[,、\uFF0C\n\r]+/).map(function (name) { return name.trim(); }).filter(function (name) { return name.length > 0; });}

function showCount() {var count = readNames().length;countText.innerHTML = '参加者の人数:<strong>' + count + '</strong>名';}

function setMessage(text) {resultText.textContent = text;resultText.classList.add('is-message');}

function setName(text) {resultText.textContent = text;resultText.classList.remove('is-message');}

function draw() {var names = readNames();

if (names.length === 0) {setMessage('参加者の名前が入っていません。名前をカンマで区切って入力してください。');return;}

if (names.length === 1) {setName(names[0]);return;}

var left = 18;drawButton.disabled = true;drawButton.textContent = '抽選中です';

var timerId = window.setInterval(function () {setName(names[Math.floor(Math.random() * names.length)]);left = left - 1;

if (left > 0) { return; }

window.clearInterval(timerId);setName(names[Math.floor(Math.random() * names.length)]);drawButton.disabled = false;drawButton.textContent = 'もう一度抽選する';}, 80);}

namesField.addEventListener('input', showCount);drawButton.addEventListener('click', draw);

  showCount();
})();
</script>
</body>
</html>

名前を書き換える場所

名前は、<textarea id='names' rows='5'> の中に書いてあります。最初は「あおい, かえで, さくら, つばさ, なお」の5人です。ここを書き換えれば、そのまま自分の企画の名前になります。画面を開いてから入力欄を直接書き換えてもかまいません。書き換えると、下の「参加者の人数」もその場で数え直されます。

区切りはカンマだけではありません。JavaScript の readNames の中で、次の4つを区切りとして扱っています。

  • , 半角のカンマ
  • 、 全角の読点
  • , 全角のカンマ
  • 改行

ですので、1行に1人ずつ並べても読めます。スマートフォンから入れるときに、カンマを探さなくてすみます。前後の空白は落とし、何も書いていないところは数えません。最後にカンマを打っても、空の名前は入りません。

押したときの動き

ボタンを押すと、draw という function が動きます。順番は次のとおりです。

  1. 入力欄から名前を読み出します。
  2. 名前が0人のときは、当たった人の欄に「参加者の名前が入っていません。名前をカンマで区切って入力してください。」と出して終わります。
  3. 名前が1人のときは、抽選をせず、そのまま出します。1人で回すと、止まるまで待たされるだけだからです。
  4. 2人以上のときは、18回、80ミリ秒ごとに名前を入れ替えます。0.08秒 × 18回で、1.4秒ほどの間だけ名前が回ります。
  5. 回り終わったら、もう一度引いて、そこで止めます。止まったときの名前が当選です。
  6. 回っている間はボタンを押せないようにして、文字を「抽選中です」に変えます。二重に押して2回引いてしまうのを防ぐためです。
  7. 止まったら、ボタンが押せる状態に戻り、文字が「もう一度抽選する」に変わります。

回っている途中に見えている名前は、最後に出る人の手がかりにはなりません。止まる直前にもう一度引き直しているからです。画面の前にいる人が、出た名前を予想できないようにしてあります。

当たった名前は result-name のところに出ます。文字の大きさは clamp(40px, 9vw, 76px) と書いてあります。画面の幅が狭いときは小さく、広いときは大きくなりますが、40ピクセルより小さくはなりません。配信の画面に重ねたときに読める大きさを残すためです。長い名前が入っても、word-break: break-word で折り返します。

ブラウザで開いたときの画面

ここで1つ、正直に書いておきます。ブラウザで単体で開いた画面は、わたくしは撮っていません。表示の確認は、OBS のブラウザソースに読み込んで行いました。撮っていない画面をここに置くと、読む人に違うことを伝えてしまいますので、置きません。

その代わりに、開いたときに何が並ぶかを、コードのとおりに書いておきます。上から順に、次の6つです。

  1. 見出しの「参加者を決めるルーレット」
  2. 説明の一行「名前をカンマで区切って並べてください。ボタンを押すと、その中から1人が選ばれます。」
  3. 「参加者の名前(カンマ区切りで入力)」という見出しと、名前の入った入力欄
  4. 「参加者の人数:5名」という数え方の表示
  5. 黄色い「抽選する」というボタン
  6. いちばん下に「選ばれた人:」と、まだ引いていないときの案内

背景は濃い色で、文字は白、当たった人の名前とボタンだけが黄色です。当たった名前は、いちばん下の枠の中に大きく出ます。

OBS のブラウザソースに置いて動かしてみた画面

次に、OBS のブラウザソースにこのファイルを読み込ませて動かしたことを書きます。手順は次のとおりです。

  1. ソースの一覧で、新規作成を選び、ブラウザを選びます。名前は分かりやすいものでかまいません。
  2. 「ローカルファイル」に印を付けて、参照のボタンから、保存した HTML のファイルを選びます。
  3. 幅と高さを決めます。この欄は、自分の配信の画面に合わせて決めてください。わたくしは、用意したカードが切れずに収まる大きさにしました。
  4. 「カスタム CSS」という欄もあります。見た目は HTML の中のスタイルで決まるように書いていますので、この欄を使わなくても表示できました。
  5. OK を押すと、配信の画面の上に重なって出ます。

読み込む前に、設定している途中の画面を撮りました。読み込む場所、幅と高さ、カスタム CSS の欄が写っています。

OBS のブラウザソースの設定画面。ローカルファイルの欄、幅と高さの欄、カスタム CSS の欄が写っている

読み込んだあと、実際に映った画面を撮りました。

OBS の画面に、参加者を決めるルーレットが映っている。名前を入れる欄、参加者の人数、抽選するボタン、選ばれた人の欄が見えている

映った画面では、名前の入った入力欄と人数が出て、ボタンを押すと1つが大きく出ました。ボタンの文字は、押している間だけ「抽選中です」に変わります。名前が大きく出ますので、配信の画面に重ねても読めます。

ここで、はっきり書いておきたいことがあります。わたくしは配信をしていません。確かめたのは、自分の OBS の中に読み込んで、そこで押したところまでです。「配信で使ってみたら、こうだった」ということは書けません。実際の配信の画面で、見ている人にどう見えたかは、わたくしは見ていません。使うときは、自分の画面で一度出して、文字が読めるかどうかを確かめてください。

こういう使い方はいかがですか

同じファイルが、3通りの場面で使えます。名前の書き方と、押す回数が変わるだけです。わたくしがその場面で実際に使ったわけではありませんので、使い方の案として読んでください。

順番を決める

参加者の名前をその人数だけ並べて、1人ずつ引きます。当たった人から順番に、という決め方です。このときは、一度出た人を消していく必要があります。消し方は、このあとの「こう広げられます」のところに書きます。

見え方としては、名前が大きく出て、下のボタンが「もう一度抽選する」に変わります。そのまま押せば次を引けますので、順番が決まるまで続けられます。何人ぶん引いたかを数えておかないと分からなくなりますので、決まった順番はどこかに書き留めておくといいと思います。

罰ゲームを割り当てる

罰ゲームの内容を名前の欄に入れて引く、という使い方です。「ものまね」「一発芸」のように書いても、そのまま大きく出ます。人の名前を出すのではなく、内容を出すだけの違いです。

このときは、当たった人が誰かは別に決めておくことになります。1回の抽選では、人と内容の両方は決められません。人を引いてから内容を引く、という順番で2回押す使い方もあります。ただし、2つの結果が同じ画面に残るわけではありませんので、どちらがどちらだったかを取り違えないようにしてください。

視聴者へのプレゼントの当選者を決める

応募してくれた人の名前を並べて引く使い方です。このときは、参加者が多いほど、名前を並べるのに手間がかかります。カンマでも、読点でも、改行でも区切れますので、応募の一覧をそのまま貼り付けて、区切りだけ整えると早いと思います。

気をつけるところが2つあります。1つは、このファイルは引いたあとに名前を消しませんので、同じ人が2回出ることがあるという点です。もう1つは、引いた結果の記録が残らないという点です。画面を閉じると消えますので、必要なら別に控えてください。どちらも、次の書き換えで変えられます。

こう広げられます:手を入れる所

ここからは、手を入れる場所を書きます。どの行を変えると何が変わるか、という形で並べます。

名前を増やす

入力欄の <textarea id='names' rows='5'> の中身を増やすだけです。仕組みの上では人数の上限はありませんが、大人数のときの動きは、わたくしは試していません。画面の高さが足りなくなるときは、rows='5' の数字を増やしてください。

一度出た人を消して、重複をなくす

今のコードは、引いたあとも名前を残します。消したいときは、抽選が終わったところで、当たった名前を入力欄から取り除きます。今は最後のところが setName(names[Math.floor(Math.random() * names.length)]); の1行になっています。その行を、次の形に置き換えます。

var picked = names[Math.floor(Math.random() * names.length)];
setName(picked);

// 当たった人を入力欄から消す
namesField.value = names
  .filter(function (n) { return n !== picked; })
  .join(', ');
showCount();

この置き換えは、わたくしは試していません。仕組みとしては、当たった名前と同じものを外して、残りをカンマでつなぎ直し、人数を数え直す、という流れです。同じ名前が2つ入っていると、その両方が消えます。1人ずつにしたいときは、名前を重ねずに入れてください。残りが0人になると、次に押したときは「参加者の名前が入っていません」と出ます。そこまでいく前に止めたいときは、残りの人数を見てボタンを押せなくする行を足すことになります。

音や色を足す

色は、スタイルの上のほうにある --accent: #ffd166; を変えると、ボタンと当たった名前の色が一緒に変わります。--accent-dark: #2b2205; はボタンの文字の色です。背景は --card: #14181f; と、body の background: #0a0d11; で決まっています。ここを変えると、配信の画面の色に合わせられます。

音は、わたくしは試していません。試していないことを、試したように書かないでおきます。考え方としては、音のファイルを HTML と同じ場所に置いて、止まったところで鳴らす1行を足します。

// 同じ場所に置いた音のファイルを鳴らす
new Audio('chime.mp3').play();

この書き方も、わたくしの手元では動かしていません。音のファイルも用意していません。使うときは、自分の画面で確かめてください。

回る速さと回数を変える

80 は、名前を入れ替える間の時間です。数字を小さくすると速く回り、大きくするとゆっくり回ります。18 は回数です。増やすと長く回ります。80 と 18 を掛けた分だけ待つことになりますので、今は1.4秒ほどです。長くしすぎると、待たされている感じが出ます。

文字の大きさを変える

当たった名前の大きさは clamp(40px, 9vw, 76px) の3つの数字で決まります。左から、これより小さくしない大きさ、画面の幅に対する割合、これより大きくしない大きさです。配信の画面が大きいときは、右の数字を上げてください。

確かめたのはどこまでか

わたくしが確かめたのは、自分の OBS に読み込んで、そこで押したところまでです。次のことは確かめていません。

  • 実際の配信の画面で、見ている人側にどう見えるか
  • 音を鳴らす書き方
  • 一度出た人を消す書き換え
  • 大人数のときの動き

これは、わたくしが配信をしていないからです。ですので、この記事には「配信で使ってみたら」という話はありません。使う前に、自分の画面で一度出して、文字が読めるかどうかを確かめてください。

借りたものの出どころ

最後に、借りたものの出どころを書きます。

自分で書いたもの

この記事に載せた HTML、CSS、JavaScript は、わたくしが書いたものです。他所のコードを写したところはありません。記事の中の文章も、わたくしが書きました。

OBS Studio

ブラウザソースは、OBS Studio という道具の中の機能です。作っているのは OBS Project です。

  • 公式サイト: https://obsproject.com/
  • GitHub: https://github.com/obsproject/obs-studio

作者の X の在りかは、わたくしが確かめられませんでした。分からないものを推測で書くと、読む人が迷いますので、ここには書きません。

Math.random

抽選に使っている命令は、JavaScript に元から入っているものです。使い方の説明は MDN のページにあります。

  • https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math/random

書体

スタイルの中に、’Hiragino Kaku Gothic ProN’、’Yu Gothic’、’Noto Sans JP’、’Meiryo’ の4つを並べました。左から順に、その機械に入っているものが使われます。書体のファイルは同梱していませんので、置き場所は挙げません。このうち Noto Sans JP は Google が作っている書体で、配布元は Google Fonts です。

  • https://fonts.google.com/

参考にした記事

今回、第三者の記事を参考にはしていません。書いたのは、手元で動かして見たことと、そのコードの中身です。参考にしたと書ける記事が無いのに、それらしい名前を並べるのはやめておきます。

確認した範囲

動かした結果として書いたのは、OBS のブラウザソースに読み込んで映った画面と、そのときに押して名前が出たことだけです。それ以外のことは書いていません。ここまで読んでくださって、ありがとうございました。

コメント

タイトルとURLをコピーしました