Google Apps ScriptでSVGを使ったイメージマップを操作 - 基礎編【GAS】

以前、jQueryのライブラリを使用しての、フリーアドレス対応「座席表アプリ」をGoogle Apps Scriptで作成しました。ただし前回のソレは弱点があり、ウィンドウの大きさ等に応じて画像をリサイズすると、設定しておいたイメージマップの座標エリアが見事にズレてしまうため、サイズ固定になってしまっていたこと。

また、Canvasを使っていた為に、文字がボヤけたりその対策をしなければならなかったりと。という事で、今回SVG画像を利用してのイメージマップで座席表の下地を作ってみようと思います。

Google Apps Scriptで座席表を作ろう - イメージマップ編【GAS】

今回使用するファイルやツール

今回はフリーでSVGの画像ファイルを作成する事のできるInkscapeを使って、土台となるファイルを作成します。SVG画像はバイナリではなくテキストで構成されたファイルである為、JavaScriptからタグの操作をする事も可能です。今回のサンプルでは以下の機能を装備します。

  • SVG画像上のタイルでマウスオーバーで色を変更
  • SVG画像上のタイルクリックでメッセージを表示する
  • SVG画像上のタイルクリックでSVG上のテキストを変更
  • 画面サイズが変更されてもSVG画像をスケールしレスポンシブ対応

次回応用編ではスプレッドシート連携して席の確保とリリース、初期化時にスプレッドシートのデータを元にタイルの色や属性を変更する処理を入れる予定です。

Google Apps ScriptでSVGを使ったイメージマップを操作 – 応用編【GAS】

前回の作品との相違点

前回の座席表アプリことイメージマップ版は、下地になる画像1枚に対して、古典的なクリッカブルマップの技術を使っていました。今回の作品はSVGグラフィックスのファイルを使ってる為

  1. クリッカブルマップと異なり、ウィンドウの大きさ変更でも自動的にSVGファイルがスケールしてくれる
  2. クリッカブルマップと異なり、リンクの範囲がズレるといった事が無い
  3. クリッカブルマップはそのエリアの定義が非常に複雑でImage Map Generatorを使って、細かく範囲を作る必要があって大変だったのが、SVGの場合はInkscapeでヴィジュアルに簡単に作成する事が可能。
  4. 基本テキストデータでHTMLのようにIDを持っており、JavaScriptで要素の操作が可能である。
  5. 前回の作品はCanvas描画であるため、文字の滲みなどが生じ対策が必要であったけれど、SVGの場合はそういった事が不要
  6. 前回の作品は複雑なライブラリを多く利用して実現していたけれど、SVGの場合はそういった事が不要

IDの割り振りと、スプレッドシート側で準備したデータに相違がなければ、SVGファイルの入れ替えでレイアウトの変更が可能となるため、作り手の負担が非常に軽減します。また、Inkscapeはオープンソースであるため、ローコストで使えるというのも利点の1つだと思います(クラウドの座席表アプリは結構いい値段をサブスクで支払う必要がある)

事前準備

ウェブを見てると、やたらとイラストレータを使って出力といった記事を多く見かけるのですが、この為に高いイラレを買ってどうこうするというのは、コスパ的に全くと言ってメリットが無いので、オープンソースであるInkscapeで実現します。結局はSVGはテキストデータであるので、本来イラレで無くて問題ありません。

SVG画像を用意する

Inkscapeで作成して書き出し

今回のアプリで一番大変な作業は、InkscapeにてSVGファイルを用意すること。ただ単純にドロー画像を用意するのではなく、以下のような手順で各パーツにIDを用意してみたり、テキストエリアを追加したりが必要になります。

今回は非常に単純な4マスを用意し、スプレッドシートのセルのアドレスに対応するIDを付与しています。

  1. 800x500のサイズのキャンバスを今回作っています。
  2. そこに四角形とテキストを1組のグループ化したパーツを4つ作成。それぞれに席の名称を入れておきます。
  3. 1つパーツを選択して、右サイドパネルの緑色のスパナのアイコンのタブ(オブジェクトのプロパティ)を開く
  4. パーツに自動でIDが割り振られてるので、ここにセルのアドレスを入れる(A001のパーツならばA1を入力)。入力したら、右下の設定を必ずクリックしないと反映しない。
  5. 各テキストにもIDを割り振っておきましょう。後でこの文字をJSから書き換えます
  6. ファイル⇒エクスポートを実行すると、右下に色々出てくる
  7. ファイル形式はPlainSVGを選択。エクスポートをクリックする
  8. 描画.svgというファイルが生成されました。

図:パーツにIDを振っている場面

書き出したSVGを修正

JavaScriptから色々操作をする上でこのままだと色々不便なので、このSVGファイルに対して色々と手を加えます。今回四角形はセルのアドレス、各テキストはtextA1といった名前、その中の文字部分はspanA1として名前を付けています。svgファイルをVSCodeなどのテキストエディタで開くと、HTMLのタグのような形で記述がされてるのがわかると思います。

  1. 今回は、svgファイルをVSCodeで開いてみます。
  2. 各パーツはgタグで括られており、id="A1"といったように割り振られているのが確認出来ます。文字のtspanにはランダムなIDが振られているので、これをspanA1といったように書き換えます。
  3. パーツをクリックした時にコマンドを実行できるようにリンクを貼ります。gタグの前後を<a>タグで括り、hrefで指定する事で、ハイパーリンクになります。以下がA1パーツの参考例です。rectのタグにclassとしてbarを指定(後でCSSでhoverの設定を加えます)。更に色を変える為に、style属性からfillを抜いて別途、rectにfill属性と色指定を追加(これが基準の色になる)
    <a href="https://www.google.com/" target="_blank">
        <g
            id="A1"
            transform="translate(0,-22.652543)">
            <rect
                style="stroke-width:0.264583"
                id="rect228"
                width="302.03387"
                height="210.16525"
                fill="#63ff8d"
                class="bar"
                x="70.474571"
                y="59.148308" />
            <text
                xml:space="preserve"
                style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:105.833px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000080;stroke-width:0.264583"
                x="91.86866"
                y="206.38983"
                id="textA1"><tspan
                    id="spanA1"
                    style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:105.833px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000080;stroke-width:0.264583"
                    x="91.86866"
                    y="206.38983">A001</tspan></text>
        </g>
    </a>
  4. リンクではなく、JavaScriptの関数を実行させたい場合には、aタグの中身を以下のように書き換える事で、自分の用意した関数を実行させることができるようになります。
    <a href="javascript:void(0);" onclick="alert('microsoft');">

    クリックすると画面遷移はせず、onclickの中身が実行される仕組みです。

  5. 実際にChromeでSVGファイルを開いてみて、クリックしたら思ったような挙動になるか確かめる
  6. SVG全体のIDにはSeat001というIDを付与しておきました。後でCSSの操作で利用します。

Inkscapeのバグ

完成されたSVG画像を元にしばらく運用し、あとからちょっと足したいなと思い、同じファイルをInkscapeで読み込ませたところ、「gタグとrectタグのIDの値は大丈夫だったけれど、tspanタグの値が全部勝手に書き換わる」というバグが存在します。その為、再度Inkscapeで処理をした場合、tspanタグのIDは全部手直しが必要です(tspan5277みたいに勝手に書き換わってしまう)。最新版のv1.2.2で確認しています。

自分の場合

  1. SVGファイルのコピーを取っておく
  2. 1つ目のSVGで新たに座席データをコピーして追加して保存する(ファイルの一番最後に書き込まれる)
  3. 2.の書き込まれた部分だけをテキストエディタで取り出しておく
  4. 各種タグのIDを変更しておく
  5. 2つ目のSVGをテキストエディタで開いて、4.のテキストを追加して保存する

という手順で最小限に押さえています。illustrator等の高価なツールならばこのような問題は起きないと思いますが、フリーではInkscape以上のSVGエディタは無いので、この部分だけは困った問題です。

スプレッドシート側の準備

作成したsvg画像のコードをHTMLに貼り付けてコントロールしても良いのですが、やはりそこは分けておいたほうが、色々と後で便利なのでGAS側にSVGの画像のコードを入れておくようにします。

SVG画像をHTMLファイルとして用意

SVG画像に前述のような手入れを加えて使いますが、このコードを以下のようにGAS側のHTMLファイルに対して貼り付けます。

  1. svg画像ファイルをVSCodeなどで開いて全部のコードをコピーする
  2. GAS側で新規にファイルを追加、HTMLを追加し、1.のコードを貼り付ける
  3. HTMLのファイルに今回はsvgmapという名称を付けました

このコードをウェブアプリケーション表示時に呼び出して、特定の位置に挿入して表示するようにしています。

図:svgデータをHTMLとして格納しておく

シートデータを整備する

今回は4マスだけの座席表アプリなので非常に単純ですが、座席表・シートデータ・ユーザリストを整備します。

座席表のほうは、単純にそれぞれのセルの幅を広げただけ。ここにはシートデータに値が入ると対象のユーザの名称が表示されるようvlookup関数が入っています。

シートデータのほうは、座席ナンバーがそれぞれセルの番地となっていて、HTML側で入力された内容を入れる場所。固定フラグは選択不可にする場合にオンにします。社員IDと割当ユーザは、入力値からユーザリストを検索した結果をここにJavaScriptにて入れる仕組みにしています。

ユーザリストは利用するメンバーの社員番号と名前を格納し、席を選択時にIDを入れてもらい、ここから検索した結果をシートデータの方に書き込みをする仕組みになっています。

図:シートデータの整備は地味に重要です

CSSで装飾

GAS側のHTML側コードに於いて、以下のようなCSSを加えて、SVGファイルのコードに対して装飾を行わせています。

<style>
      svg {
        width: 100%;
        height:300px;
      }
      svg a path{
        transition: fill 0.3s linear;
      }

      .bar {
          pointer-events: all;
      }

      .bar:hover { 
          opacity: 0.7;
          fill: red;
      }

      #map{
        width:100%;
        height:1px;
      }
</style>

SVGがテキストデータによるファイルであるため、このようなテクニックが利用できるわけです。hover時にはopacityで半透明にし、svgファイルの横のスケールを100%としていますので、ウインドウの大きさが変わっても大丈夫。

縦のスケールについては、後述のソースコードにてウィンドウのサイズを計算してそこから丁度よい高さをjQueryで実行しCSSを動的に変更しています。

class名がbarのものについては、pointer-eventsでallを加えないとホバー時の色変更が効かないので入れておく。さらにhoverでは色指定をしておくと、マウスオーバーで色が変わるようになる。

ソースコード

HTML側コード

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

    <style>
      svg {
            width: 100%;
            height:300px;
      }

      svg a path{
            transition: fill 0.3s linear;
      }

      .bar {
            pointer-events: all;
      }
      .bar:hover { 
            opacity: 0.7;
            fill: red;
      }

      #map{
            width:100%;
            height:1px;
      }
    </style>

    <script>
      //リサイズ時にウィンドウにフィットさせる
      window.onresize = function(){
            setGridHeight();
      }
			
      //gridのサイズを自動でウィンドウにフィットする
      $(document).ready(function () {
                  setGridHeight();
      });

      //mapの高さを自動補正する関数
      function setGridHeight() {
            var layoutHeight = $(window).height() - 25;
            $('#svg5').css('height', layoutHeight + 'px');
      }

      //SVGファイルデータを呼び出し挿入する
      function init(){
            google.script.run.withSuccessHandler(onSuccess).svgload();
      }

      function onSuccess(data){
            $("#map").html(data);
            setGridHeight();
      }

      //メッセージを表示する
      function messenger(msg,address){
            //メッセージ表示
            alert(msg);

            //文字書き換え
            let textaddr = "#span" + address;
            $(textaddr).html(msg);
      }
    </script>

  </head>
  <body onLoad="init()">
    <!-- SVG画像貼り付け場所 -->
    <div id="map"></div>
  </body>
</html>
  • ウィンドウリサイズ時イベント用にsetGridHeight関数を用意。初期化後やリサイズ時に実行し、SVG画像をスケールさせています
  • クリック時イベント用にmessenger関数を用意。クリックするとalert表示とspanA1等のIDのテキストを書き換えています。

GAS側コード

//外部貼り付け用として表示
function doGet(){
  //スクリプトレットを使えるようにしておく
  var url = 'https://officeforest.org/wp/library/mashroom.png';
  var html = HtmlService.createTemplateFromFile("seatchart")
    .evaluate()
    .addMetaTag('viewport', 'width=device-width, initial-scale=1, maximum-scale=2.0, user-scalable=yes')
    .setTitle('座席表SVG')
    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
    .setFaviconUrl(url);
  return html;  
}

//SVGデータを取得して返す
function svgload(){
  //HTMLサービスでCSSを取得
  var svg = HtmlService.createHtmlOutputFromFile('svgmap')
            .setSandboxMode(HtmlService.SandboxMode.IFRAME).getContent();

  return svg;
}
  • svgmapというHTMLファイルにSVGデータを入れてあります。
  • seatchartが前述のHTMLになり、ロード時にsvgloadをgetContentで呼び出して、svgmap内のSVGデータをHTML側に返し、挿入させています。

表示サンプル

フルサイズはこちらをクリック

クリックすると、メッセージが表示されて、表示されてるテキストが変更されます。また、HoverをCSSで効かせてるので、赤い表示になります。opacityで色味の不透明度を調整すれば、どぎつい色にならず、もうちょっと滑らかになるのではないかと。

関連リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)