OutSystemsでお絵描きアプリのような描画ソフトを実現

# はじめに
OutSystemsを利用した開発で、Web上で図形など簡単な描画をしたいというリクエストを見かけるようになりました。何等かの申請業務で地図の場所や写真の箇所などを手書きで記入する例が多いようです。そこで、これを実現するためのアプリ「DrawingCanvas」をOutSystemsのForgeにアップしたのでご紹介します。

「DrawingCanvas」の特徴は、HTML canvas上のデータをオブジェクトとして扱うので、画像データが軽量です。また、一般的なショートカットキー（コピー：Ctrl+c、移動：→←↑↓　など）や画像ファイルのドラッグ＆ドロップ、そしてモバイル端末での2点タッチによるピンチズームなどにも対応しており、様々な環境における利便性を追求しています。
今回は、Googleマップにマーキングをする例で使い方を整理します。

# DrawingCanvas
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/bbe18bd3-4c30-e355-2abf-5f3a77cf9465.png)](https://www.outsystems.com/forge/component-overview/11860/drawingcanvas)

https://www.outsystems.com/forge/component-overview/11860/drawingcanvas

# Googleマップを切り取る
GoogleマップをJavaScriptでキャプチャするにはStaticマップでなければならなかったり、制限が多いものです。
Windowsのショートカットを使えば、これはすぐに解決します。[Windows]＋[Shift]＋[S]でGoogleマップの切り取りたい場所をキャプチャします。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/f38ae512-87db-c236-3c42-19537606e653.png)

# DrawingCanvasの背景登録画面を開く

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/ecc1dd94-f746-0943-fdf4-d4ecd3cd38ed.png)



# クリップボードのデータをキャンバスに貼り付け保存
DrawingCanvasは、[Ctrl]＋[V]で画像をそのまま貼り付けることができます。
また、すでに画像ファイルがある場合には、ファイルのドラッグ＆ドロップでも画像を貼り付けることができます。
保存時にはサムネイルも生成されるので、一覧表示時などの負荷を軽減しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/1a72302a-cb8c-ae98-3ffd-cf5d03b565c6.png)

#### ドラッグ＆ドロップの方法
![img2022-03-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/ee4712c4-52bb-bd86-662f-331e5babc1a6.png)


# URL経由で背景画像を設定
新規描画登録画面で、上記で登録した背景画像をURL経由で設定することができます。
背景画像として設定された画像はキャンバスに固定化されます。
なお背景画像を直接、キャンバスにBase64データ（バイナリをテキスト化したもの）として貼り付け固定化することはできますが、パフォーマンスの観点からは、背景画像など比較的大きなイメージは、この機能のように事前にデータベースへアップロード登録してそのURL（今回はREST API）を参照設定することをお勧めします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/2dbffe8b-fa58-eb53-c574-5636c21f2a8b.png)


また、このDrawingCanvasでは背景画像データをデータベースに保存していますが、格納するファイルの容量が大きくなる場合には、データベースよりコスト（性能、費用）を最適化できるファイルサーバーへの保存も検討のひとつになります。

### 描画キャンバスで背景画像設定ボタンをクリック
![img2022-02-17 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/d37cd4a6-dd53-f930-9741-307ef9f1d0c5.png)


### 背景画像の選択
ここで選択した画像は、キャンバスデータ内に背景画像としてURLのリンクだけが埋め込まれるので、データも肥大化しません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/df60dc0d-c562-d613-f978-0cd3954ebbcb.png)



# 図形を描く
ツールバーのボタンをクッリクすることで図形を描くことができます。
![img2022-02-09 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/aea03f42-72a4-8dc7-f76f-3ff62e31b7e0.png)


パレットには透過色を登録することも可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/191f5886-a7a2-fe83-976d-88360a648b81.png)


図形の円に吹き出しと矢印を描画してみました。円図形内は、透過色で塗つぶしています。
ちなみに、この描画に使用しているデータサイズは4KBです。座標やカラーの情報等だけをJsonテキストデータとして保持するので軽量です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/577e918a-9fcd-71c7-a65f-9e38d895d35c.png)


# 主な機能
## ツールバー
![toolbar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/00364bdc-c3a2-6eea-bcf4-aec11899d404.png)




| 機能 | 説明 | 
|:-----------|------------|
| 元に戻す| ひとつ前の状態に戻す| 
| やり直す     | ひとつ後の状態に戻す| 
| 範囲選択| オブジェクトを範囲選択| 
| 手書き     | フリーハンドで自由な線を描画| 
| 直線| 2点間に直線を描画| 
| 矢印| 2点間に矢印を描画| 
| 長方形| 2点間に囲まれた矩形を描画| 
| 楕円| 2点間に囲まれた楕円を描画| 
| 枠線幅| 図形の枠線幅を指定| 
| 枠線の色| 図形の枠線色を指定| 
| 塗つぶし色| 図形内を塗つぶす色を指定| 
| テキスト| テキストを入力| 
| 文字のサイズ| 文字のサイズ| 
| 文字の色| 文字の色| 
| 画像アップロード| 画像をアップロードし貼り付け| 
| 全アンロック| オブジェクトのロックを全解除| 
| 全て削除| オブジェクトをすべて削除| 
| PNGに保存| キャンバスをPNGで保存| 

## コンテキストメニュー
オブジェクトを右クリックすることで、コンテキストメニューを表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/c716467f-1bc3-ce34-379b-016a534017ec.png)


| 機能 | 説明 | 
|:-----------|------------|
| コピー| 対象のオブジェクトをコピー| 
| 貼り付け  | 対象のオブジェクトを貼り付け| 
| 最前面へ移動| 対象のオブジェクトを最前面へ移動| 
| 最背面へ移動| 対象のオブジェクトを最背面へ移動|
| グループ化| 対象のオブジェクトをグループ化（ネスト不可）|
| グループ化解除| 対象のオブジェクトをグループ化解除|
| オブジェクトロック| オブジェクトを選択できないようにロック| 
| 背景画像に設定| 背景画像として設定し画像をロック| 
| キャプチャ| 対象のオブジェクトをPNGファイルに保存|
| 削除| 対象のオブジェクトを削除|

※キャプチャ機能が何気に便利です。Qiitaの画像資料を作成する時などもスクリーンショットで切り取った画像をキャンバス上で加工し対象のオブジェクトだけを選択して右クリックすれば直ぐにPNG画像（デフォルトは透過画像）として保存できます。
Windowsの **Snipping Tool** でも似たようなことは出来ますが、テキスト文字のタイピングや矢印の描画などは出来ないので、私はこの部分を気にいっています。

## ショートカットキー
| 機能 | 説明 | 
|:-----------|------------|
| Ctrl＋C| 対象のオブジェクトをコピー<br>※クリップボード経由になるので、他のキャンバスにデータを貼り付けることが可能| 
| Ctrl＋V | 対象のオブジェクトを貼り付け| 
| Ctrl＋Ａ | 対象のオブジェクトを全件選択| 
| Del| 対象のオブジェクトを削除| 
| ↑ ↓ → ← | 対象のオブジェクトを移動|
| Tab |オブジェクト間を移動|
| Shift＋Tab |オブジェクト間を逆移動|
| Ctrl＋ホイール | キャンバスのズームイン/ズームアウト|

### ズームアウト時
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/0cc25cea-4a82-13c5-250d-d7754b799eac.png)

# なめらかな曲線

DrawingCanvasでは、手書き曲線をサポートしています。
よくある手書きソフトは、ドットの連続値で表現するものが多くマウスの振動を拾ったりして線がカクカクしたものになりがちですが、本製品では、近似曲線のベジエ曲線を採用していますので、曲線が非常に滑らかです。
![FullSize_11651_SmoothLine.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/80459821-d22e-ff40-a949-d46f70ef7ae5.png)




# 開発部品

DrawingCanvasの入力パラメータであるCanvasDataは、図形の形状や位置情報などを記述した独自のJsonフォーマットでやり取りされます。

```独自Json
{"version":"5.2.0","objects":[{"type":"ellipse","version":"5.2.0","originX":"left","originY":"top","left":316,"top":113,"width":156,"height":160,・・・・・}
```
テキストデータが長くなることも想定されるので、データ保存時にはテキストをバイナリ化してデータベースに登録します。
なお、このバイナリデータは、GetCanvasJsonクライアントアクションによりJson形式のバイナリ値としてキャンバスから取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/4037b6a4-7f7b-b38a-bdf7-e09bc4660d41.png)

#### ■ DrawingCanvasブロック
キャンバス上に図形や画像データなどを加工するためのUI部品です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/b08edab3-4346-8ea9-8cfe-432ca16ea613.png)


| 入力パラメータ | 必須| 初期値| 説明 | 
|:-----------|------------|------------|-----------|
| CanvasData| No| なし| キャンバス（JSON形式）のバイナリデータ| 
| CanvasId  | No| c | ひとつの画面上に複数のCanvasが存在する場合のユニーク値 | 
| Width | No| 800 |キャンバスの幅（px） | 
| Height    | No| 500 | キャンバスの高さ（px）| 
| BackgroundColor| No| なし | キャンバスの背景色|
| BackgroundImageUrl| No| なし|  キャンバスの背景画像<br>画像のURLまたはデータURIスキームの指定（データ：image / png; base64など）| 
| FitBackgroundImage| No| 0 |背景画像をキャンバスサイズへ自動で拡大縮小<br> 0 : なし<br> 1 : 幅と高さを合わせる<br> 2 :比率を維持しながら横幅を合わせる<br> 3 : 比率を維持しながら高さを合わせる| 
| MaxFileSize| No| 1000 | 画像をアップロードする時の画像の最大容量（KB）| 
| MaxHistory| No| 10 | Undo/Redoの繰り替えし最大回数| 
| UserId| No| なし | 描画オブジェクトにユーザーIdを設定<br>誰がどのオブジェクトを作成したかを記録する時に利用| 
| IsView| No| False | 編集なしのビューモード（True）| 
| License| No| なし | ライセンスキー | 



#### ■ ResizableCanvasブロック

任意のサイズにキャンバスデータを描画するためのUI部品です。
一覧表示などで、キャンバスを小さく表示したいときに利用します。大きさを変えるには、WidthやHeightはDrawingCanvasを保存したときの正規の値を設定し、**ResizableCanvasブロックをコンテナーで囲み、そのコンテナーに幅などのサイズを指定**します。

※一覧では、ひとつの画面に複数のUI部品が配置されることになるので、CanvasId入力パラメータはレコードIDなどを使いユニークな値にする必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/974ca692-6443-031e-b964-5a9dca290e34.png)

| 入力パラメータ | 必須| 初期値| 説明 | 
|:-----------|------------|------------|-----------|
| CanvasData| No| なし| キャンバス（JSON形式）のバイナリデータ| 
| CanvasId  | No| c | ひとつの画面上に複数のCanvasが存在する場合のユニーク値 | 
| Width | No| 800 |キャンバスの幅（px）※この値でサイズ変更はしません | 
| Height    | No| 500 | キャンバスの高さ（px）※この値でサイズ変更はしません| 
| License| No| なし | ライセンスキー | 

## エンコード
Json形式のキャンバスデータは、データベース更新参照時にデフォルトではutf-8でエンコード/デコードされます。通常の日本語であればこれで問題ありませんが特殊な文字コードが含まれる場合には、エンコードをサイトプロパティーで変更できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486327/e842ade0-5adc-5d61-b987-d94fc5fb19ef.png)



# まとめ
画像の処理は、けっこう面倒だったりしますが、ドラッグ＆ドロップや便利なショートカットをサポートしているので、ユーザーの作業効率アップに貢献することが可能です。
なおオブジェクトは、透過色が利用可能で、透過画像を簡単に作成することも出来ます。（経験的に透過画像ファイルを作るのは意外と面倒）

汎用的な機能であれば、新機能として追加する予定ですので、コメントいただけると助かります。