JointJS解説　〜Highlighters編〜

## Highlightersとは

Highlightersは名前の通り要素をハイライトするなどの視覚効果を付与できるJointJSの機能です。
オン・オフの切り替えができるので、マウスイベント発生時の視覚効果をつけるときに有用な機能となります。

## 使用方法

視覚効果の種類によって呼び出すAPIは異なりますが、基本的な使い方としては各APIの`add()`メソッドを使用すると視覚効果が追加されます。また、`remove()`メソッドを呼ぶと視覚効果が削除されます。
以下は、要素をクリックしたときにハイライトを表示し、領域外をクリックしたときにハイライトを削除するサンプルです。

### コード例

```js
// ハイライトを付与
paper.on('cell:pointerdown', function(cellView) {
  joint.highlighters.mask.add(cellView, 'root', 'my-mask-highlighter', {deep: true});
});

// ハイライトを削除
paper.on('blank:pointerdown', function() {
  graph.getCells().forEach(function(cell) {
    joint.highlighters.mask.remove(cell.findView(paper));
  });
});
```

### 実行結果

![Untitled.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129526/feb2f32e-5803-0954-dfcf-c920c4e17b07.gif)

Highlightersでは視覚効果の付与対象を引数に渡されたCellのViewで特定します。
各種イベントではコールバック関数内でイベントが発生したCellのViewを取得できますのでそれを利用すると良いでしょう。

逆に、上の例の削除処理では、イベントが発生した場所とハイライト操作を行う対象のCellが一致していません。このような場合はコールバック関数の引数以外の方法でCellのViewを取得する必要が出てきますので注意しましょう。

### idの指定

場合によっては複数の視覚効果を同時につけつつ、片方のみを消したい状況があります。そういった場合には、`add()`メソッド内の第3引数で指定したidを使用することで、該当するidのみに`remove()`を適用するといったことが可能になります。

#### コード例

```js
paper.on('cell:pointerdown', function(cellView) {
  // テキストの周りに消えるハイライトを表示
  joint.highlighters.mask.add(cellView, 'label', 'id-deletable', {
    deep: true, 
    attrs: {
      'stroke-width': 3,
      stroke: 'blue'
    }
  });
  // 四角形の周りに消えないハイライトを表示
  joint.highlighters.mask.add(cellView, 'body', 'id-not-deletable', {
    deep: true, 
    attrs: {
      'stroke-width': 2,
      stroke: 'red'
    }
  });
});

paper.on('blank:pointerdown', function() {
  graph.getCells().forEach(function(cell) {
    joint.highlighters.mask.remove(cell.findView(paper), 'id-deletable');
  });
});
```

#### 実行結果

<img width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129526/c73c5430-74e9-e6d8-3c08-143aee921832.gif" />

## Highlightersの種類

Highlightersにはいくつかの種類があります。つけたい視覚効果によって適切な種類のHighlightersを選択して使用します。

### `mask` / `stroke`[^1]

要素を囲う線を表示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129526/9b291a9c-9c13-fce5-e290-ac314b38cb20.png)

### `opacity`

要素を半透明にします。
内部的には要素のclassに`joint-highlight-opacity`のクラスが付与されているので、半透明の内容をカスタマイズする場合は自前のCSSで`.joint-highlight-opacity`に対してCSSを定義すればOKです。[^2]

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129526/61d3190f-6aac-f0e8-fe9a-12a09700e3bb.png)

### `addClass`

任意の名称のclassを要素に追加します。
classを追加しただけでは何も起きませんので、classに対してのCSS設定は自前で準備する必要があります。自由なCSSのハイライトを定義できるということになりますので汎用性は高いです。
名前が`addClass`ですが`remove()`するときも`addClass.remove()`という書き方になるのでご注意ください。

#### コード例

```main.js
paper.on('cell:pointerdown', function(cellView) {
  joint.highlighters.addClass.add(cellView, 'body', 'addclass-highlighter', 
  {
    className: 'background-blue'
  });
});
```

```custom.css
.background-blue {
  fill: lightblue;
}
```

#### 実行結果


<img width="400px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129526/910a1729-9b0e-ec80-bbcb-42985f48bfb4.gif" />

## まとめ

今回はインタラクティブな視覚効果を追加するためのHighlightersを紹介しました。お手軽にオンオフの切り替えができる点が結構便利ですので、イベント発生時の視覚効果をつけるときは積極的に利用しましょう。

※この記事は [JointJS Advent Calendar 2023](https://qiita.com/advent-calendar/2023/jointjs) の記事です。他の記事を読む場合はカレンダーのページを参照してください。

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

[^1]:2つの挙動には若干の差異があります。差異の内容については[`mask`のドキュメント](https://resources.jointjs.com/docs/jointjs/v3.7/joint.html#highlighters.mask)に記載されている補足文をご確認ください。
[^2]:内部動作を利用した上書きが大丈夫なのかと不安になりそうですが、公式ドキュメント記載の方法ですのでご安心ください。なお、デフォルトのJointJS CSSでは`.joint-highlight-opacity`に対して`opacity: 0.3;`のみが設定されていました。