AppSheetのimage型カラムで動的svg画像を表示する方法

## はじめに
[AppSheet](https://about.appsheet.com/home/)はGoogleスプレッドシートをベースに業務アプリを開発するのにとても便利なノーコード開発ツールですが、ビュー画面を自由自在にカスタマイズ…というわけにはいかないところが歯がゆく感じられることも多いです｡

少しでもビュー画面の表現の幅を広げるために、image型のカラムに表示させる画像を工夫したい。そんなとき、AppSheetクリエーターコミュニティのフォーラムに「Dynamic SVG graphics」という投稿を見つけました。

[Dynamic SVG graphics](https://www.googlecloudcommunity.com/gc/Tips-Tricks/Dynamic-SVG-graphics/td-p/367578) (AppSheet Creators Community)

投稿者のJonathonさんのアイデアは、image型のカラムにSVG形式で画像を記録すれば、データと連動した画像を生成して表示できるというものでした。


## SVG形式で円グラフを描く
そのアイデアを起点にリプライでMike_Aさんが円グラフの描き方をまとめてくださって、いい感じに表示するコードを提供してくれてます｡
それにテキスト表示も追加して、サイズなんかアレンジしてしまいましたが、60%の円グラフを描くコードがこちらです｡

```:プレーンなSVGコード
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">

  <!-- 背景のGrey円 -->
  <circle
    cx="100" cy="100" r="80" fill="none" stroke="lightgrey"
    stroke-width="30"
  />
  
  <!-- 前景のNavy円 -->
  <circle
    cx="100" cy="100" r="80" fill="none" stroke="Navy"
    stroke-width="30"
    stroke-dasharray="502.65"
    stroke-dashoffset="201.06"
    transform="rotate(-90 100 100)"
  />
  <!--  stroke-dashoffset = 502.65 × (1 - 割合)  -->

  <!-- テキスト表示 -->
  <text fill="Navy" font-family="sans-serif" font-size="300%" font-weight="bold" x="50" y="115">60%</text>
</svg>
```

これを実際のデータベース内のカラムにあるFormulaのExpression Assistantに入力する際には「**data:image/svg+xml;utf8,**」という文言を付加して、ダブルクォーテーションを二重にしなければならないところがあり少々面倒です。（たぶんよい方法がありますけれど、それは後日）以下のコードをコピペしてください｡

```:AppSheetに貼り付けるときのコード
"data:image/svg+xml;utf8,
<svg xmlns=""http://www.w3.org/2000/svg"" viewBox=""0 0 200 200"">

  <!-- 背景のGrey円 -->
  <circle
    cx=""100"" cy=""100"" r=""80"" fill=""none"" stroke=""lightgrey""
    stroke-width=""30""
  />
  
  <!-- 前景のNavy円 -->
  <circle
    cx=""100"" cy=""100"" r=""80"" fill=""none"" stroke=""Navy""
    stroke-width=""30""
    stroke-dasharray=""502.65""
    stroke-dashoffset=""201.06""
    transform=""rotate(-90 100 100)""
  />
  <!--  stroke-dashoffset = 502.65 × (1 - 割合)  -->

  <!-- テキスト表示 -->
  <text fill=""Navy"" font-family=""sans-serif"" font-size=""300%"" font-weight=""bold"" x=""50"" y=""115"">60%</text>
</svg>"
```
<img width="450" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47334/f5c87cb5-02d2-4c73-846f-d46f251b2e71.png">

結果はこんな感じです｡
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47334/af81a998-3818-4d44-a856-e0aef1bcdf25.png">

もしも他のパーセンテージで描画したい場合は、stroke-dashoffsetの数値を変更し、テキスト表示の文字を変更します｡

たとえば30%（割合0.3）ならば…
stroke-dashoffset = 502.65 × (1 - 0.3) = **351.855**
となります。


## 動きのある円グラフを描く
冒頭のクリエーターフォーラムのやりとりでは、プログレスバーをSVG形式で表現するための参考となるウェブサイトも紹介されていました｡

[Animated SVG Radial Progress Bars](https://daverupert.com/2018/03/animated-svg-radial-progress-bars/)

これを参考にすればグラフの描画時に動きを付けられそうでした｡
こういうときは生成AIの助けを借りて、これらの情報を盛り込んだSVG形式のコードを作ってもらうのが一番です｡

```:プレーンなSVGコード
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">

  <style>
    .circle-bg {
      stroke: lightgrey;
    }
    .circle-meter {
      stroke: navy;
      stroke-dasharray: 502.65;
      stroke-dashoffset: 502.65;
      animation: draw 1s ease-out forwards;
    }
    @keyframes draw {
      to {
        stroke-dashoffset: 201.06; /* こちらを変更 */
      }
    }
  </style>

  <!-- 背景のGrey円 -->
  <circle
    class="circle-bg"
    cx="100" cy="100" r="80" fill="none"
    stroke-width="30"
  />

  <!-- 前景のNavy円（アニメーション付き）-->
  <circle
    class="circle-meter"
    cx="100" cy="100" r="80" fill="none"
    stroke-width="30"
    transform="rotate(-90 100 100)"
  />

  <!-- 中央のテキスト -->
  <text fill="Navy" font-family="sans-serif" font-size="240%" font-weight="bold" text-anchor="end" x="155" y="113">60%</text>
</svg>
```

これで出だし１回だけ動きのある描画が行なわれます｡あとはキャッシュされちゃうので、コードが書き変わるまで静止したグラフですけれど｡

## 他のカラムの数値と連動した円グラフを描く

さて、長い前置きを経て、ようやく一番やりたかったことへ。他のカラム（ここではdecimal型）の数値をもとに円グラフとテキストを描画するSVG形式のコードをExpression Assistantに貼り込む形で記述しましょう｡

Decimal型のカラムは［Percentage］というカラム名にしたとします｡

```:AppSheetに貼り付けるときのコード
if(ISBLANK([Percentage]),
"",
CONCATENATE(
"data:image/svg+xml;utf8,",
"<svg xmlns=""http://www.w3.org/2000/svg"" viewBox=""0 0 200 200"">

  <style>
    .circle-bg {
      stroke: lightgrey;
    }
    .circle-meter {
      stroke: navy;
      stroke-dasharray: 502.65;
      stroke-dashoffset: 502.65;
      animation: draw 1s ease-out forwards;
    }
    @keyframes draw {
      to {
        stroke-dashoffset: ", 
        TEXT(502.65 * (1 - [Percentage] / 100)), ";
      }
    }
  </style>

  <!-- 背景のGrey円 -->
  <circle
    class=""circle-bg""
    cx=""100"" cy=""100"" r=""80"" fill=""none""
    stroke-width=""30""
  />

  <!-- 前景のNavy円（アニメーション付き）-->
  <circle
    class=""circle-meter""
    cx=""100"" cy=""100"" r=""80"" fill=""none""
    stroke-width=""30""
    transform=""rotate(-90 100 100)""
  />

  <!-- 中央のテキスト -->
  <text fill=""Navy"" font-family=""sans-serif"" font-size=""240%"" font-weight=""bold"" text-anchor=""end"" x=""155"" y=""113"">", 
  [Percentage], "%", "</text>
</svg>"
))
```

［Percentage］が空であれば、image型のカラムも空っぽ（""）にします。［Percentage］の数値があれば、その数値をもとに計算して描画されます｡たとえばフォームビューで見るとこんな感じです｡

<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47334/37c72576-f5af-42e0-9e80-02e2f39d0938.png">

フォームビューでimage型カラムを表示していると、数値に連動してリアルタイムに描画されるのですが、そこでアニメーションが済んでしまって、実際の閲覧画面ではアニメーションしてくれないかもしれません｡ちょっと寂しいですが…｡

## 注意点
これもフォーラムのやりとりで書かれていたことですが、SVG内で色を指定するとき、「#」（ハッシュタグ）記号を使った色指定表記だとAppSheetでエラーになってしまいます。それ以外の色指定方法を試してください｡


## （追記）棒グラフの場合

```:プレーンなSVGコード
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 20" width="200" height="30">
  <style>
    .bg {
      stroke: lightgray;
      stroke-width: 30;
      fill: none;
    }

    .meter {
      stroke: Navy;
      stroke-width: 30;
      fill: none;
      stroke-dasharray: 200;
      stroke-dashoffset: 200;
      animation: fill-bar 1s ease-out forwards;
    }

    @keyframes fill-bar {
      to {
        stroke-dashoffset: calc(200 - (70 * 2));
        /* stroke-dashoffset = calc(200 - (percentage * 2)) */
      }
    }
  </style>
  <path class="bg" d="M0 10 L200 10" />
  <path class="meter" d="M0 10 L200 10" />
</svg>
```