chart.js を活用した円グラフのカスタムユニットを作る方法
一般的にグラフをサイトに貼り付けるのであれば、上記のようにエクセルで描いたグラフをキャプチャした画像を用意して画像ユニットにアップするかと思いますが、エクセルなどを使う事なくユニットに数値などを設定するだけでグラフが描けるようなユニットの作成を目指してみようと思います。
グラフを描くことができる JavaScript ライブラリ「chart.js」
グラフの表示には「chart.js」という JavaScript ライブラリを利用します。円グラフだけでなく、棒グラフや折れ線グラフなども設定次第では描くことができます。
カスタムユニット
テキストユニットではテキストの入力欄が1つ、画像ユニットであれば画像を1枚アップできるような単機能なユニットになりますが、カスタムユニットを利用するとユニットに自由にカスタムフィールドやカスタムフィールドグループを設定することができます。
今回のグラフであれば、以下のようにグラフ名、サイズ、配置の設定と、グループでラベル・色・値を複数設定しグラフ表示に必要な情報を全てユニット内に登録できるような入力欄を実装します。
カスタムユニットは、少しだけカスタムフィールドと記述が違いますので、カスタムフィールドメーカー を利用する際には、ラジオボタンでカスタムユニット・カスタムユニット(フィールドグループ)を選択してください。
管理ページ側の実装
/themes/*利用テーマ*/admin/entry/unit/extend.html
<!-- BEGIN custom_chart --> <h2 class="acms-admin-admin-title2">円グラフ</h2> <table class="acms-admin-table-admin-edit" style="margin-bottom: 10px;"> <tr> <th>グラフ名 <i class="acms-admin-icon-tooltip js-acms-tooltip" data-acms-tooltip="(必須項目)グラフ毎に固有なグラフ名を設定してください。"></i> </th> <td> <input type="text" name="chartid{id}" value="{chartid}" class="acms-admin-form-width-mini" placeholder="例)〇〇な円グラフ"> <input type="hidden" name="unit{id}[]" value="chartid{id}"> </td> </tr> <tr> <th>横幅 <i class="acms-admin-icon-tooltip js-acms-tooltip" data-acms-tooltip="グラフの横幅をピクセル単位で設定します。"></i> </th> <td> <input type="text" name="chartwidth{id}" value="{chartwidth}" class="acms-admin-form-width-mini" placeholder="例)300"> <input type="hidden" name="unit{id}[]" value="chartwidth{id}"> <span>px</span> </td> </tr> <tr> <th>位置 <i class="acms-admin-icon-tooltip js-acms-tooltip" data-acms-tooltip="グラフの位置を設定します。"></i> </th> <td> <select name="chart_margin{id}" class="acms-admin-form-width-mini"> <option value="left" {chart_margin:selected#left}>左寄せ</option> <option value="center" {chart_margin:selected#center}>中央寄せ</option> <option value="right" {chart_margin:selected#right}>右寄せ</option> </select> <input type="hidden" name="unit{id}[]" value="chart_margin{id}"> </td> </tr> </table> <table class="js-fieldgroup-sortable adminTable acms-admin-table-admin-edit"> <thead class="acms-admin-hide-sp"> <tr> <th class="acms-admin-table-left acms-admin-admin-config-table-item-handle"> </th> <th class="acms-admin-table-left">ラベル <i class="acms-admin-icon-tooltip js-acms-tooltip" data-acms-tooltip="ラベル名を設定します。"></i> </th> <th class="acms-admin-table-left chart_color_bk-js">色 <i class="acms-admin-icon-tooltip js-acms-tooltip" data-acms-tooltip="各グラフの色を設定します。"></i> </th> <th class="acms-admin-table-left">値 <i class="acms-admin-icon-tooltip js-acms-tooltip" data-acms-tooltip="値を設定します。"></i> </th> <th class="acms-admin-table-left acms-admin-admin-config-table-action">削除</th> </tr> </thead> <tbody> <!-- BEGIN custom_chart:loop --> <tr class="sortable-item"> <td class="item-handle"> <i class="acms-admin-icon-sort"></i> </td> <td> <input type="text" name="chart_label{id}[]" value="{chart_label}" placeholder="例)ラベル" class="acms-admin-form-width-full"> </td> <td class="chart_color_bk-js"> <input type="color" name="chart_bk_color_single{id}[]" value="{chart_bk_color_single}" placeholder="例)#70a6fff" class="acms-admin-form-width-full chart_color_single-js"> </td> <td> <input type="text" name="chart_data_single{id}[]" value="{chart_data_single}" placeholder="例)30" class="acms-admin-form-width-full chart_data_single-js"> </td> <td> <input type="button" class="item-delete acms-admin-btn-admin acms-admin-btn-admin-danger" value="削除"> </td> </tr> <!-- END custom_chart:loop --> <tr class="sortable-item item-template"> <td class="item-handle"> <i class="acms-admin-icon-sort"></i> </td> <td> <input type="text" name="chart_label{id}[]" value="" placeholder="例)ラベル" class="acms-admin-form-width-full"> </td> <td class="chart_color_bk-js"> <input type="color" name="chart_bk_color_single{id}[]" value="" placeholder="例)#c41134" value="#c41134" class="acms-admin-form-width-full chart_color_single-js"> </td> <td> <input type="text" name="chart_data_single{id}[]" value="" placeholder="例)30" class="acms-admin-form-width-full chart_data_single-js"> </td> <td> <input type="button" class="item-delete acms-admin-btn-admin acms-admin-btn-admin-danger" value="削除"> </td> </tr> </tbody> <tfoot> <tr> <td colspan="5"> <input type="button" class="item-insert acms-admin-btn-admin" value="追加"> </td> </tr> </tfoot> </table> <input type="hidden" name="@custom_chart{id}[]" value="chart_label{id}"> <input type="hidden" name="unit{id}[]" value="chart_label{id}"> <input type="hidden" name="@custom_chart{id}[]" value="chart_border_color_single{id}"> <input type="hidden" name="unit{id}[]" value="chart_border_color_single{id}"> <input type="hidden" name="@custom_chart{id}[]" value="chart_bk_color_single{id}"> <input type="hidden" name="unit{id}[]" value="chart_bk_color_single{id}"> <input type="hidden" name="@custom_chart{id}[]" value="chart_data_single{id}"> <input type="hidden" name="unit{id}[]" value="chart_data_single{id}"> <input type="hidden" name="unit{id}[]" value="@custom_chart{id}"> <!-- END custom_chart -->
管理ページの実装
コンフィグ / エントリー / 編集設定 / ユニット追加ボタン
新しいボタンを追加するために(追加)ボタンをクリックし、拡張 を選択し「モード」に上記のコードで <!-- BEGIN custom_chart --> に書かれているものと同様の custom_chart と入力し、「ラベル」は利用者に分かるようなラベルを設定します。今回の場合には、グラフ か 円グラフ のように記述するといいでしょう。
コンフィグ / エントリー / ユニット設定
ユニット追加ボタンの設定ができたら、管理画面のユニット設定の一番下に「グラフ」が増えていますので、(グラフ)のボタンをクリックしてください。そうする事で以下のようにグラフユニットが1つ追加されます。
ここまで設定すると、管理画面側の設定は完了になります。
表示側のテンプレートの実装
/themes/*利用テーマ*/include/unit/extend.html
<!-- BEGIN unit#custom_chart --> <!-- BEGIN_SetRendered id="js-chart" --> <script src="/js/in-view.min.js"></script> <script src="/js/Chart.bundle.min.js"></script> <script> $(function() { $('canvas').each(function() { var id = $(this).attr('id'); var ctx = $(this).get(0).getContext('2d'); var $template = $(this).next(); var option = JSON.parse($template.html()); inView('#'+ id).once('enter', function() { new Chart(ctx, option); }); }); }); </script> <!-- END_SetRendered --> <div class="chart" style="<!-- BEGIN chartwidth:veil -->max-width: {chartwidth}px;<!-- END chartwidth:veil --><!-- BEGIN_IF [{chart_margin}/eq/center] --> margin: 0 auto 25px {chart_left};<!-- ELSE_IF [{chart_margin}/eq/left] --> margin: 0 auto 25px 0;<!-- ELSE_IF [{chart_margin}/eq/right] --> margin: 0 0 25px auto;<!-- END_IF -->"> <canvas width="400" height="400" id="chart-{utid}"></canvas> <template data-id="chart-{utid}"> { "type": "pie", "data": { "labels": [<!-- BEGIN custom_chart:loop --><!-- BEGIN glue -->,<!-- END glue -->"{chart_label}"<!-- END custom_chart:loop -->], "datasets": [{ <!-- BEGIN chart_bk_color:veil -->"backgroundColor": [ <!-- BEGIN custom_chart:loop --><!-- BEGIN glue -->,<!-- END glue -->"{chart_bk_color_single}"<!-- END custom_chart:loop --> ],<!-- END chart_bk_color:veil --> "data": [<!-- BEGIN custom_chart:loop --><!-- BEGIN glue -->,<!-- END glue -->{chart_data_single}<!-- END custom_chart:loop -->] }] } } </template> <p class="chart-name" style="text-align: center; margin-top: 5px;">{chartid}</p> </div> <!-- END unit#custom_chart -->
グラフを描くための chart.js の読み込みと合わせて、表示されたタイミングでグラフが動くように jQuery.inview.js を読み込んでいます。
/themes/*利用テーマ*/include/head/js.html
<!-- GET_Rendered id="js-chart" -->
標準のテーマであれば js.html 無い場合には <head> タグのどこかに、上記の1行を追加ください。
外部の JavaScript ライブラリを活用する際の最適な実装方法
今回は chart.js を読み込んでいるわけですが、<head> に使うか使わないか分からないのに毎回読み込んだり、複数のグラフユニットを追加したユニットのループの中に複数回の読み込むのもいい実装とは言えません。
そこで、SetRendered を利用します。<head> に書く内容をユニットのループ内に書き、その部分を
<!-- BEGIN_SetRendered id="js-chart" --> <!-- END_SetRendered -->
で囲む事で、そこには表示させず変数化することができます。これを複数回実行されても同じ場所に同じ内容が書かれることから複数になることはありません。そして、最終的には <head> 内に書かれている
<!-- GET_Rendered id="js-chart" -->
に、SetRendered で登録されている変数が置きかわり <head> に、JavaScript の記述が1回だけ編集されます。このようにカスタムユニットで JavaScript を読み込む必要があるような場合には、SetRendered と GET_Rendered を活用してください。