Yappli Tech Blog

株式会社ヤプリの開発メンバーによるブログです。最新の技術情報からチーム・働き方に関するテーマまで、日々の熱い想いを持って発信していきます。

YappliオリジナルビンゴをGASで作った話

こんにちは!株式会社Yappli CorporateITの辻村です。

こちらは2023年度のYappliアドベントカレンダー2枚目の12/24の記事でもあります🎄
なんとアドベントカレンダー1枚目が全部埋まって、2枚目に突入です👀🎉

オリジナルBINGO

今年のYappliの納会の二次会で、ビンゴを開催することになりました 🎁

恒例のビンゴですが、普通のビンゴにはもうみんな慣れていてつまらない...?
Yappliっぽさを出したい!
社員のみなさんに楽しんでほしい!

という思いで、コンテンツもりもりなオリジナルビンゴを作りました 🎉

こんな感じ↓
右上の数字でビンゴをしつつ、社員紹介&あらかじめ集めたコメントを載せています。

[💡point💡]

ビンゴは1~75の数字で構成されているので、75人分のスライドが必要です。

全社のSlackチャンネルでGoogleフォームに回答を任意で募り、社員のコメントを集めました。
また、2023年度の新入社員は全員スライドに入れました!(社員のみなさんに覚えてもらえるよう🎉)

※スライドがとってもおしゃれですが、納会準備仲間のデザイナー小川さんにデザインを作ってもらいました!天才!✨✨
ビンゴ画像は、Keynote(GoogleスライドやPowerPointでもOK)で作ったスライドを、画像で書き出して利用しています。

↓こんな動的Webサイトが完成形です。(注意⚠️:再生すると音が出ます)

さっそく、作り方について紹介して行きます。
ぜひいろんな方に真似していただきたいので、非エンジニアでも作れるよう細かく説明して行きたいと思います!

BINGO(動的Webサイト)の作り方

GASの作成

まず、Googleドライブを開き、下記手順でGASを新規作成します。

 → 

「ファイル」の右上の「+」から、ファイルを新規作成します。

  • bingo.gs(種類:スクリプト)
  • index.html(種類:HTML)
  • css.html(種類:HTML)


それぞれのファイルにコードを書いて行きます。

bingo.gs

/**
 * Webページの構築
 */
function doGet() {
  var html = HtmlService.createTemplateFromFile('index')
    .evaluate()
    .setTitle('Yappli BINGO! 2023') // TODO:変更してください① (Webサイトのタブに表示されるタイトルです)
    .setSandboxMode(HtmlService.SandboxMode.IFRAME);

  return html;
}

/**
 * ビンゴを回す
 */
function playBINGO() {
  // 画像の中からランダムで一つ選ぶ
  var newId = getRandomImage();
  var imageUrl = "https://drive.google.com/uc?id=" + newId;

  // 画像の取得に失敗したら(時々ネットの調子等で失敗するので)
  if(newId === undefined){ 
    newId = getRandomImage();
    imageUrl = "https://drive.google.com/uc?id=" + newId;

    // それでも失敗したらエラー画面を返す
    if(newId === undefined){
      return "https://drive.google.com/uc?id=XXXXXXXXX" // TODO:変更してください② (エラー画面を用意して下さい)
    }
  }
  return imageUrl;
}

/**
 * ランダムに新しい画像を選ぶ
 */
function getRandomImage(){
  var scriptProperties = PropertiesService.getScriptProperties();
  var fileList = JSON.parse(scriptProperties.getProperty("FILE_LIST"));

  // 乱数を取得
  let num = Math.floor( Math.random() * (fileList.length + 1) );
  // 配列から乱数番目のIDを取得
  var id = fileList[num]
  // 取得したIDを配列から消す
  fileList.splice(num, 1)

  // 編集後の配列をプロパティにセット
  scriptProperties.setProperties({'FILE_LIST': JSON.stringify(fileList)});

  // ランダムで選んだ画像のIDを返却
  return id;
}

/**
 * 画像フォルダの中にある画像を全て読み込む
 */
function setFiles(){
  //対象フォルダ内のすべてのファイルを取得するプログラム
  var id = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // TODO:変更してください③ (画像を格納しているGoogleドライブのフォルダIDを入れて下さい)
  var target = DriveApp.getFolderById(id);
  var files = target.getFiles();

  var fileList = []
  while (files.hasNext()) {
    var file = files.next();
    fileList.push(file.getId())
  } 

  console.log("fileList:" + fileList)
  console.log(fileList.length + "個のファイルをセットしました")

  var scriptProperties = PropertiesService.getScriptProperties();
  
  scriptProperties.setProperties({
    'FILE_LIST': JSON.stringify(fileList),
  });
}
functionの役割
  • doGet() :Webページを構築するときに自動で実行されます。GASでWebページを作るときのお約束的なfunctionです。
  • playBINGO():Webページ右下のNextボタンを押すと呼び出される。ビンゴが実行されます。
  • getRandomImage():ランダムで1つの画像を選ぶ。(一度選ばれた画像は二度と選ばれない)
  • setFiles():ビンゴの画像(75枚全て)をセットする。(ビンゴ前に実施する。ビンゴが途中まで進んでいても、setFiles()を実行すると全てリセットされます。)
TODOの対応をする

上記のコードに記載されてる // TODO を対応して下さい。

// TODO:変更してください① (Webサイトのタブに表示されるタイトルです)

↓ここに表示される文言です。



// TODO:変更してください② (エラー画面を用意して下さい)

ネットワークの調子で画像の取得に失敗することがあります。
また、ストックした画像が全てなくなったとき(全ての画像がビンゴで表示された後)にも表示する画面です。

参考:Yappliでは、このような画面を用意しました。(小川さんありがとう✨)

20231221020923

URLの挿入方法

※ 画像や音楽などはGoogleドライブに配置して下さい。

挿入したいファイルの「共有設定」で、権限を「自分のみ」の状態から、他の人もアクセスできる権限に変更して下さい。その上でリンクを取得します。

コピーしたリンクの

https ://drive.google.com/file/d/ XXXXXXXXXXXXXXXX/view?usp=sharing

XXXXXの部分がファイルのIDです。これをコードのXXXXXの部分に埋め込んでください。
(※ id以外の文字列 "https ://drive.google.com/file/d/" と、"/view?usp=sharing" は使わない)



// TODO:変更してください③ (画像を格納しているGoogleドライブのフォルダIDを入れて下さい)

ランダムで表示させたいビンゴの画像(75枚)のみが入っているフォルダを作ります。(※表紙や、エラー画面は入れないでください。)

フォルダ全体の権限を、「自分のみ」の状態から、他の人も見れる権限にする。

フォルダIDの取得の仕方↓この赤枠の文字列を、TODO の XXXXX の部分に入力して下さい。


index.html

<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
     <?!= HtmlService.createHtmlOutputFromFile('css').getContent(); ?>
  </head>
  <body>
    <img id="dynamicImage" style="position: relative;" src="https://drive.google.com/uc?id=XXXXXXXXXXX" alt="image"> // TODO:変更してください① (一番最初に表示したい画像のURLを入れて下さい)
    <button  class="btn" onclick="next()">Next</button>
    <audio id="drumroll" style="display: none;" controls>
      <source src="https://drive.google.com/uc?id=XXXXXXXXXXX" type="audio/mpeg"> // TODO:変更してください② (ドラムロール(ドコドコ...)の音楽ファイルのURLを入れて下さい。)
    </audio>
    <audio id="hitSound" style="display: none;" controls>
      <source src="https://drive.google.com/uc?id=XXXXXXXXXXX" type="audio/mpeg"> // TODO:変更してください③ (ドン!!の音楽ファイルのURLを入れて下さい。)
    </audio>
    
    <script>
      function next() {
        // 待機画像(ぐるぐるのGIF)
        document.getElementById('dynamicImage').src = "https://drive.google.com/uc?id=XXXXXXXXX"; // TODO:変更してください④ (ぐるぐる、、の時のGif画像を入れてください)
        
        // ドラムロールならし始める
        var drumroll = document.getElementById('drumroll');
        drumroll.play();

        // Hitサウンド
        var hitSound = document.getElementById('hitSound');
        hitSound.currentTime = 0;

        // GAS ファイル内の関数を呼び出す
        google.script.run.withSuccessHandler(function(result) {
          // 2秒待ってから画像のURLをセットする
          setTimeout(function() {
            document.getElementById('dynamicImage').src = result;
            drumroll.pause();
            drumroll.currentTime = 0;
            hitSound.play();

            // 1秒待ってから hitSound を流す
            setTimeout(function() {
              hitSound.play();

              // 0.2秒待ってから hitSound を停止
              setTimeout(function() {
                hitSound.pause();
                hitSound.currentTime = 0;
              }, 200);
            },1000);
          }, 2000);
        }).playBINGO(); // bingo.gs内の functionを呼び出している。
      }
    </script>
  </body>
</html>
解説

scriptタグで囲まれている範囲(function next())は、「Next」ボタンが押されたときに呼び出されます。
<button class="btn" onclick="next()"><span style="color: #673ab7">Next</span></button>

画面の中にサウンドが埋め込まれていますが <audio id="drumroll" style="display: none;" controls>
このサウンドを流す/止めるの制御も、function next()内で行っています。
html drumroll.play(); html drumroll.pause();

function next()の最終行で、bingo.gsのfunction playBINGO() を呼び出しています。

TODOの対応をする

上記のコードに記載されてる // TODO を対応して下さい。

// TODO:変更してください① (一番最初に表示したい画像のURLを入れて下さい)

ページを開くと最初に表示されるタイトル画像のURLです。
URLの挿入方法を参考にして下さい。

参考:Yappliではこの画像を使いました

20231221103945

// TODO:変更してください② (ドラムロール(ドコドコ...)の音楽ファイルのURLを入れて下さい。)
// TODO:変更してください③ (ドン!!の音楽ファイルのURLを入れて下さい)
// TODO:変更してください④ (ぐるぐる、、の時のGif画像を入れてください)

URLの挿入方法を参考にして下さい。

参考:YappliではこのGif画像を使いました(デザイナー小川さん作🙏)


css.html

<style>
  body {
    font-family: Arial, sans-serif;
    margin: 0; /* ボディの余白をゼロに設定 */
    padding: 0;
    font-size: 0;
  }

  #dynamicImage {
    width: 100vw; /* ビューポートの幅いっぱいに設定 */
    height: 100vh; /* ビューポートの高さいっぱいに設定 */
    object-fit: cover; /* 画像をアスペクト比を保ちつつ全画面に表示 */
  } 
 
  .btn { /* 右下に小さく表示される、Nextボタンのデザインです。 TODO: 画像のデザインに合わせて適宜変更して下さい */
    display: inline-block;
    position: absolute; 
    margin: 1px;
    padding: 4px 8px;
    right: 1px;
    bottom: 1px;
    cursor: pointer;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-transition: all 0.3s;
    transition: all 0.3s;
    text-align: center;
    vertical-align: middle;
    text-decoration: none;
    letter-spacing: 0.1em;
    color: #ffffff;
    background-color: #212529;
    border-radius: 0.5rem;
    font-size: 8px;
    font-weight: 700;
    line-height: 1.5;
  }

</style>
解説

画面のデザインをここで定義しています

.btn { } ブロックで、Nextボタンの位置や形や色を設定していますが、現状の設定だと下記画像のようなボタンになります。
※ 右下の小さい黒いボタンです。
この画面をプロジェクターでスクリーンに投影してビンゴ大会をするので、スライドの邪魔にならないように小さく角に追いやりました。押す人(PC操作してる人)だけがわかればよかったためです。

このボタンのデザインを変更するには上記の.btn{ } ブロック内の設定変更で対応可能です⭕️
(🔍 「CSS ボタン」とかで検索するとCSS初心者さんでも比較的簡単に使える、ボタンのデザインが多数Hitします。)


これ

 (拡大したもの)


デプロイをする

コードが書けたら、デプロイをします。

GAS画面右上の「デプロイ」→「新しいデプロイ」

「種類の選択」は「ウェブアプリ」、「アクセスできるユーザー」を自分以外の人もアクセスできる権限に変更

「アクセスを承認」→この画面の後の画面も、承認して下さい。

デプロイ完了。ウェブアプリのURLをコピーしてから、「完了」を押します。別タブでコピーしたURLを開きましょう。(まだビンゴはできません)

デプロイ後にコードを編集したら、再度デプロイをしないと変更は反映されません。
GASの仕様で、デプロイのたびにWebページのURLが新しくなります。


ビンゴ画像(75枚)の仕込みをする

ここで設定したフォルダに、ビンゴの画像(1~75)のみが入ってることを確認する。
その上で、bingo.gsのsetFiles()を実行します。
画像の通りに、bingo.gsを開いた状態で「setFiles()」を選択して、「実行」を押します。

完成

これで、ビンゴの準備が整いました。 Webページをひらいて、右下のNextを押すとビンゴを回すことができます。

※ビンゴ大会の本番前には、setFiles()を実行してビンゴのリセットをお忘れなく!!

番号(1~75の連番)さえ合っていれば、他の情報は何を載せてもいいわけなので、汎用性が高いです。いろんなビンゴを作れちゃいます🤩


さいごに

ビンゴカードも、市販のビンゴカードにYappliシールや社長の顔シールを貼って、みんなでデコりました。

準備はとても大変だったのですが、 当日はかなりの盛り上がり、、、!!豪華景品のおかげでもあると思うのですが、楽しそうに盛り上がる社員の方々の笑顔が見れて、頑張って準備してきてよかったな〜!と思いました。

当日はネットワークトラブル(用意したポケットwifiではネットが遅すぎて、ビンゴの画面切り替えが遅すぎる)や、音響トラブル(ビンゴ音がスピーカーに流れない)などもありました。
ネットワークは会場(カフェ)の備え付けWifiに接続したら解決し、音響はスピーカーとPCを接続し直す&ビンゴ画面再読み込みで解決しました。

それ以外にも細々とトラブルや変更点ありましたが、チームのみんなが臨機応変に対応してくれて、目から鱗でした。その結果の大成功だったと思います!!!

準備の期間もやることが地味に結構多かったので、チームのみんなで分担して作れたから実現できたなと本当におもいます。

この記事で、オリジナルビンゴを開催したい方のお役に立てたら幸いです 🫶