JointJS解説　～Paper編～

この記事は [JointJS Advent Calendar 2023](https://qiita.com/advent-calendar/2023/jointjs) の 4日目の記事です。

https://qiita.com/advent-calendar/2023/jointjs

この記事ではJointJSの重要な要素 `Paper` について書きます。

## Paperとは

PaperはSVGの図を描画する「用紙・領域」を扱うオブジェクトです。実際はHTMLのどの要素を用紙とするかを指定するための`el`オプションと、描画対象を管理するGraphを指定するための`model`オプションを指定してインスタンス化します。

以下の例の場合、HTMLの`<div id="paper-element">`の内部にJointJSがSVGの要素を埋め込む挙動となります。

``` index.html
<body>
  <div id="paper-element"></div>
</body>
```

``` js
const graph = new joint.dia.Graph({}, {cellNamespace: joint.shapes});
const paper = new joint.dia.Paper({
  el: document.getElementById("paper-element");,
  model: graph
});
```

Graphと同じく、一度インスタンス化したPaperはJointJSでの描画が終わるまで保持しておく必要があります。

## Paper生成時に指定できるオプション（抜粋）

Paperには用紙・領域の設定を行うための様々なオプションが用意されています。代表的なオプションを抜粋して紹介します。(`el`と`model`は省略します）

|キー名|意味|
|--|--|
|`width` / `height`| Paperの領域の幅と高さを指定します。`el`に指定したHTML要素に領域の設定があった場合でもこちらの値で上書きされます。 |
|`async`|`true`を指定するとCellを非同期レンダリングします。非同期のほうがレンダリングのパフォーマンスが上がりますが、**一部のレンダリングを前提としたメソッドが正しく動作しなくなる**場合があります。詳しくは[公式ドキュメント](https://resources.jointjs.com/docs/jointjs/v3.7/joint.html#dia.Paper.prototype.options.async)をご確認ください。|
|`background`|背景を指定します。背景には色または画像を指定でき、サイズや繰り返しの設定をオプションで指定できます。`false`を指定すると背景なしとなります。|
|`cellViewNamespace`| 要素の定義を参照する名前空間を指定します。基本的にはGraphで指定した`cellNamespace`と同じ値を設定すればOKです。 |
|`gridSize`|グリッドのサイズを指定します。描画後のCell操作時にはグリッドに合わせた位置の単位で移動できるため、位置を揃えたオブジェクトの配置をする場合は適切なグリッドサイズを指定します。|
|`interactive`|描画後のCellに対してドラッグ＆ドロップ等の操作を許可するかどうかの指定。`false`を設定すると操作が不許可となり描画後の要素は固定されます。|
|`overflow`|`true`を指定すると、Paperの領域を超えた座標の要素もレンダリングを行います。`false`の場合は領域外は切り取られてしまいます。例えばSVG要素に対して`overflow: scroll`を指定するなど、領域外の要素も見せるような動作を想定する場合は`true`を指定するべきでしょう。|
|`preventContextMenu`|`true`の場合、描画したSVG上での右クリックメニューを表示捺せ無くします。デフォルト値は`true`です。右クリックメニューの表示を許可する場合は明示的に`false`を指定する必要があります。|


## ウィンドウイベントの処理

Paparにはウィンドウイベントを処理させることができます。使用できるイベントは公式ドキュメントの[dia.Paper.events](https://resources.jointjs.com/docs/jointjs/v3.7/joint.html#dia.Paper.events)に列挙されています。

多くのイベントには `cell:` / `link:` / `element:` / `blank:` のプレフィックスがついたキーが別々に用意されていますが、これはイベントの観測範囲を表しています。

`cell:`の場合はPaper上の全てのCell（=Linkと要素両方）を対象にします。同様に`link:`はPaper上の全てのLinkのみを、`element:`はPaper上の全ての要素のみを対象にします。Paper上の「全ての」Linkや要素が対象となってしまうことに注意してください。個別の要素にイベントを定義する場合は、カスタム要素を定義するなどして要素側にイベントの処理を書く必要があります。

`:blank`はPaper上で逆に何も描画がない位置を表します。

```js
paper.on('cell:pointerclick', {
  console.log('何かのcellがクリックされました')
});

paper.on('blank:mouseover', function() {
  console.log('何もないところでマウスが動いています')
});
```

## Paperの代表的なメソッド

Paperの持つメソッドの中で、よく使用するメソッドを紹介します（生成時に指定できるオプション設定値を取得・変更するようなメソッドもそれぞれ用意されていますが、ここでの紹介は割愛しました）。

### `findViewsFromPoint(point)`

引数に指定された座標点に存在するCellを検索します（実際はCellに紐づくViewが取得されます）。[^2]

座標点ではなく四角形領域の区画を指定する`findViewsInArea(rect [, opt])`というメソッドもあります。

### `fitToContent([opt])`

Paperの領域サイズを、Paper内に含まれるCellが専有している領域に合わせて合わせます。何も指定しないとピッタリ収まるサイズが適用されますが、引数のオプションで`minWidth`や`padding`などを指定できますので、必要な余白を確保することはできます。

### `freeze()`

非同期状態のPaperのレンダリングを一時停止します。通常、Paperに紐づくGraphのCell情報が変更されるとPaperはそれをすぐに反映しようとしますが、その動作がfreeze中は起きません。[^1]複数のセルを同時に追加する必要がある場合はこのメソッドを呼び出して停止させます。`paper.unfreeze()`メソッドを呼び出すと、freeze中のGraphの変更を全てPaperに適用します。


[^2]: 他のAPIでは`HitTest`と命名されていることが多いように感じます。
[^1]: freezeしている方がパフォーマンスが良さそうな気がしますが、未検証です。検証できたら更新するかもです。

### まとめ

JointJSではGraphと同様に重要な要素のPaperについての解説でした。Paperにオプションやイベントを設定することでリッチな図形描画ができるようになります。
次の記事では実際に描画する要素について書いていきます。