kintoneアプリで抽選カスタマイズ

著者名:三宅 智子(サイボウズ株式会社)

目次

はじめに

キャンペーンなど当選者の抽選をする時、エクセルで関数を作っていた方々に朗報です!それkintoneで楽にできるのでは?ということで、抽選カスタマイズの実装方法を紹介します。

完成イメージ

応募者データをkintoneに溜めて、抽選します。抽選結果は2パターンでお知らせするように工夫します!
レコードは全件取得するように実装しているので、応募者数が多くなっても大丈夫です。(一人1回応募が前提)

  • SweetAlertでポップアップ
  • 電光掲示板風に表示

デモ環境

デモ環境で実際に動作を確認できます。
https://dev-demo.cybozu.com/k/297/ (External link)

ログイン情報は cybozu developer network デモ環境で確認してください。

手順

  1. 抽選への応募者データを集めるkintoneアプリの作成
  2. SweetAlertでポップアップカスタマイズ
  3. 電光掲示板風カスタマイズ

kintoneのアプリ作成

応募者データを溜めるための抽選アプリをkintoneで作成します。

  1. 以下のフィールドがある「抽選」アプリを作成してください。アプリの作成方法は こちらのヘルプ (External link) を参照してください。

    フィールドタイプ フィールド名 フィールドコード
    文字列(1行) 名前 name
    文字列(1行) コメント comment
  2. サンプルのデータを数件登録しておきましょう。

JavaScript/CSSカスタマイズ

それではここからカスタマイズをしていきます。これから実装するJavaScriptとCSSファイル以外にも、3つのライブラリ( Cybozu CDN)を使うので、まずはそれらを「アプリの設定画面 > JavaScript / CSSでカスタマイズ」に設置します。

  • jQuery
    • https://js.cybozu.com/jquery/3.2.1/jquery.min.js
  • SweetAlert 2
    • https://js.cybozu.com/sweetalert2/v7.3.5/sweetalert2.min.js
    • https://js.cybozu.com/sweetalert2/v7.3.5/sweetalert2.min.css
  • Font Awesome
    • https://js.cybozu.com/font-awesome/v4.7.0/css/font-awesome.min.css

以下のようにJavaScriptファイルとCSSファイルを分けて設置してください。(random_lottery.jsとrandom_lottery.cssはのちほど設置します)

ここまで下準備ができたら、本題の抽選カスタマイズに移ります。今回は2ステップあるので、順に見ていきましょう!サンプルコードのポイント解説については、次の「サンプルコード解説」で記載しています。

Step1. SweetAlertでポップアップ

まず1つ目は、抽選結果をSweetAlertでポップアップ表示をします。

以下のJavaScriptとCSSファイルを「アプリの設定 > JavaScript/CSSカスタマイズ」にアップロードして保存してください。

random_lottery.js

一覧画面にある抽選ボタンをクリックした際、作成した抽選ロジックに基づいて抽選し、SweetAlertでポップアップ表示するためのJavaScriptファイルです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/*
 * Random lottery sample program
 * Copyright (c) 2018 Cybozu
 *
 * Licensed under the MIT License
*/

jQuery.noConflict();
(($) => {
  'use strict';

  // レコード一覧イベントを取得
  kintone.events.on('app.record.index.show', (event) => {
    // ボタン増殖防止
    if (document.getElementById('lottery-button') !== null) {
      return;
    }

    // 抽選ボタン作成
    const menu = kintone.app.getHeaderMenuSpaceElement();
    const $lotteryButton = $('<button class="lottery-button" id="lottery-button" title="抽選"><i class="fa fa-gift" aria-hidden="true"></i></button>');

    // ボタンクリック時の操作
    $lotteryButton.click(() => {
      // レコード全件取得
      const fetchRecords = (appId, opt_offset, opt_limit, opt_records) => {
        const offset = opt_offset || 0;
        const limit = opt_limit || 100;
        let allRecords = opt_records || [];
        const params = {
          app: appId,
          query: 'order by レコード番号 asc limit ' + limit + ' offset ' + offset
        };
        return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', params).then((resp) => {
          allRecords = allRecords.concat(resp.records);

          if (resp.records.length === limit) {
            return fetchRecords(appId, offset + limit, limit, allRecords);
          }
          return allRecords;
        });
      };

      // 抽選実行
      return fetchRecords(kintone.app.getId()).then((allRecords) => {
        // 抽選ロジック作成
        const num = allRecords.length;
        const rand = Math.floor(Math.random() * num);
        const name = allRecords[rand].name.value;

        // SweetAlertで当選者表示
        return swal({
          title: name + 'さん当選です!😆',
          text: 'おめでとうございます☆*:.。. o(≧▽≦)o .。.:*☆',
          timer: 3000,
          showConfirmButton: false
        });
      });
    });
    $(menu).append($lotteryButton);
  });
})(jQuery);
random_lottery.css

抽選ボタン要素用のCSSファイルです。ボタン自体は、Font Awesomeを使ってkintoneの標準的なボタンと見た目を同一にしています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 * Random lottery sample program
 * Copyright (c) 2018 Cybozu
 *
 * Licensed under the MIT License
*/

/** 抽選ボタン **/
.lottery-button {
    /* color: #a8a8a8; */
    color: #f5a1dc;
    font-size: 32px;
    display: inline-block;
    padding: 0 16px;
    height: 48px;
    border: 1px solid #e3e7e8;
    background-color: #f7f9fa;
    text-align: center;
}

/** 抽選ボタンホバー時 **/
.lottery-button:hover {
    /* color: #3498db; */
    color: #f33bb6;
}

ピンクの抽選ボタンをクリックしてみてください。ポップアップ表示がされたでしょうか?

Step2. 電光掲示板風カスタマイズ

2つ目は、SweetAlert表示の後に、電光掲示板も加える工夫をします。今回はCSSのアニメーションを使って実装しています。

下のファイルを、Step1で作成したJavaScriptとCSSファイルに上書きして保存してください。

random_lottery.js

SweetAlert表示の後に、電光掲示板が入り込んでくるようにするJavaScriptファイルです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
 * Random lottery sample program
 * Copyright (c) 2018 Cybozu
 *
 * Licensed under the MIT License
*/

jQuery.noConflict();
(($) => {
  'use strict';

  // レコード一覧イベントを取得
  kintone.events.on('app.record.index.show', (event) => {
    // ボタン増殖防止
    if (document.getElementById('lottery-button') !== null) {
      return;
    }

    // 抽選ボタン作成
    const menu = kintone.app.getHeaderMenuSpaceElement();
    const $lotteryButton = $('<button class="lottery-button" id="lottery-button" title="抽選"><i class="fa fa-gift" aria-hidden="true"></i></button>');

    // ボタンクリック時の操作
    $lotteryButton.click(() => {
      // レコード全件取得
      const fetchRecords = (appId, opt_offset, opt_limit, opt_records) => {
        const offset = opt_offset || 0;
        const limit = opt_limit || 100;
        let allRecords = opt_records || [];
        const params = {
          app: appId,
          query: 'order by レコード番号 asc limit ' + limit + ' offset ' + offset
        };
        return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', params).then((resp) => {
          allRecords = allRecords.concat(resp.records);

          if (resp.records.length === limit) {
            return fetchRecords(appId, offset + limit, limit, allRecords);
          }
          return allRecords;
        });
      };

      // 抽選実行
      return fetchRecords(kintone.app.getId()).then((allRecords) => {
        // 抽選ロジック作成
        const num = allRecords.length;
        const rand = Math.floor(Math.random() * num);
        const name = allRecords[rand].name.value;

        // SweetAlertで当選者表示
        return swal({
          title: name + 'さん当選です!😆',
          text: 'おめでとうございます☆*:.。. o(≧▽≦)o .。.:*☆',
          timer: 3000,
          showConfirmButton: false
        }).then(() => {
          // marqueeで当選者表示
          const headerSpace = kintone.app.getHeaderSpaceElement();
          const $boardDiv = $('<div id="board" class="board"></div>');
          const $boardDivInner = $('<div id="board-inner" class="board-inner"></div>');
          $boardDivInner.html(name + 'さん当選です!おめでとうございます☆*:.。. o(≧▽≦)o .。.:*☆');

          headerSpace.innerHTML = null;
          $boardDiv.append($boardDivInner);
          $(headerSpace).append($boardDiv);
        });
      });
    });
    $(menu).append($lotteryButton);
  });
})(jQuery);
random_lottery.css

電光掲示板の要素用のCSSファイルです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/*
 * Random lottery sample program
 * Copyright (c) 2018 Cybozu
 *
 * Licensed under the MIT License
*/

/** 抽選ボタン **/
.lottery-button {
    /* color: #a8a8a8; */
    color: #f5a1dc;
    font-size: 32px;
    display: inline-block;
    padding: 0 16px;
    height: 48px;
    border: 1px solid #e3e7e8;
    background-color: #f7f9fa;
    text-align: center;
}

/** 抽選ボタンホバー時 **/
.lottery-button:hover {
    /* color: #3498db; */
    color: #f33bb6;
}

/** マーキーさせたい部分 */
.board {
    width: 100%;
    height: 55px;
    text-align: center;
    font-size: 40px;
    font-weight: bold;
    background-image: radial-gradient(#0C94F5 10%, transparent 20%), radial-gradient(#F5C20C 10%, transparent 20%);
    background-color: #fcfcfc;
    background-size: 20px 20px;
    background-position: 0 0, 10px 10px;
    overflow: hidden; /* スクロールバーが出ないように */
    position: relative; /* マーキーの内容部分の位置の基準になるように */
}
/* マーキーの内容部分の高さ確保 */
.board::after {
    content: "";
    white-space: nowrap;
    display: inline-block;
}
/* マーキーさせたい部分(内側) */
.board > .board-inner {
    position: absolute;
    top: 0;
    white-space: nowrap;
    animation-name: marquee;
    animation-timing-function: linear;
    animation-duration: 20s;
    animation-iteration-count: infinite;
}
/* マウスオーバーでマーキーストップ */
.board > .board-inner:hover {
    animation-play-state: paused;
    cursor: default;
}
/** マーキーアニメーション */
@keyframes marquee {
    0% { left: 100%; transform: translate(0); }
    100% { left: 0; transform: translate(-100%); }
}

ピンクの抽選ボタンクリックでポップアップ表示の後に、電光掲示板が現れたら成功です!

サンプルコード解説

カスタマイズのポイントを解説します。

Promiseを使ってレコードの全件取得

kintone REST APIにおけるGETメソッドの一括取得は、500件までという制限があるので、レコード全件取得できるように処理を回す必要があります。
ここでは「 offset の制限値を考慮した kintone のレコード一括取得について」を参考に、Promiseを用いて全件取得できるようにします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// レコード全件取得
const fetchRecords = (appId, opt_offset, opt_limit, opt_records) => {
  const offset = opt_offset || 0;
  const limit = opt_limit || 100;
  let allRecords = opt_records || [];
  const params = {
    app: appId,
    query: 'order by レコード番号 asc limit ' + limit + ' offset ' + offset
  };
  return kintone.api(kintone.api.url('/k/v1/records', true), 'GET', params).then((resp) => {
    allRecords = allRecords.concat(resp.records);

    if (resp.records.length === limit) {
      return fetchRecords(appId, offset + limit, limit, allRecords);
    }
    return allRecords;
  });
};

// 抽選実行 以下省略
// return fetchRecords(kintone.app.getId()).then((allRecords) => {

ランダムに抽選を行うロジックの作成

Math.random()関数 (External link) Math.floor()関数 (External link) を用いて、乱数を作成して抽選します。

1
2
3
4
// 抽選ロジック作成
const num = allRecords.length;
const rand = Math.floor(Math.random() * num);
const name = allRecords[rand].name.value;

Promiseを使えるようにSweetAlert 2を利用

SweetAlert 2以前だとPromiseをうまく扱えないので、今回はPromise内でも使えるように最新のSweetAlert 2を利用します。

1
2
3
4
5
6
7
8
9
// SweetAlert2 で当選者表示
return swal({
  title: name + 'さん当選です!😆',
  text: 'おめでとうございます☆*:.。. o(≧▽≦)o .。.:*☆',
  timer: 3000,
  showConfirmButton: false
}).then(() => {
  // 省略
});

マーキー表示用に工夫

電光掲示板風な見た目を作る(マーキー表示)ためにCSSでアニメーションをつけます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/** マーキーさせたい部分 */
.board {
    width: 100%;
    height: 55px;
    text-align: center;
    font-size: 40px;
    font-weight: bold;
    background-image: radial-gradient(#0C94F5 10%, transparent 20%), radial-gradient(#F5C20C 10%, transparent 20%);
    background-color: #fcfcfc;
    background-size: 20px 20px;
    background-position: 0 0, 10px 10px;
    overflow: hidden; /* スクロールバーが出ないように */
    position: relative; /* マーキーの内容部分の位置の基準になるように */
}
/* マーキーの内容部分の高さ確保 */
.board::after {
    content: "";
    white-space: nowrap;
    display: inline-block;
}
/* マーキーさせたい部分(内側) */
.board > .board-inner {
    position: absolute;
    top: 0;
    white-space: nowrap;
    animation-name: marquee;
    animation-timing-function: linear;
    animation-duration: 20s;
    animation-iteration-count: infinite;
}
/* マウスオーバーでマーキーストップ */
.board > .board-inner:hover {
    animation-play-state: paused;
    cursor: default;
}
/** マーキーアニメーション */
@keyframes marquee {
    0% { left: 100%; transform: translate(0); }
    100% { left: 0; transform: translate(-100%); }
}

おわりに

イベントなどの抽選でご活用いただけるとうれしいです。ぜひCSSをいじりながら、自分好みの抽選アプリを作ってみてください!

注意事項

  • 画面を更新すると電光掲示板の表示を停止できます。更新しない限りは、常に流れている状態になります。
information

このTipsは、2018年1月版kintoneで動作を確認しています。