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 で動作を確認しています。