AngularでSVG使おうぜ！

本記事は [Angular Advent Calendar 2017](https://qiita.com/advent-calendar/2017/angular) の21日目の記事です。

---

みなさん、SVG使っていますか～！

HTMLと同じように文書構造を表現できるSVGですが、Angularで利用するにあたって、その利便性や注意点にはどのようなモノがあるのでしょうか？

# Angularとの親和性
svgファイルをイメージタグで読み込んだりして利用する分には特に旨味はなく、「eventListnerでonclickを取得できるベクター画像」くらいの機能性しか持たず、特にAngularとのシナジーを感じるようないいことはないかもしれません。。。:tired_face:

しかし、SVGはHTMLテンプレート上にそのまま記述でき、描写されるという特徴を持っています。
このため、バインディングやディレクティブなど、Angularを象徴する機能のほとんどがHTMLのテンプレートと同じように、遜色なく利用することができます。

SVGには、HTML/CSSだけでは実現できないような、見た目を装飾する数多くの属性やフィルターなどの仕組みが準備されております。 これらの仕組みとAngularの機能を組み合わせれば、最強のインターフェース/エクスペリエンスが簡単に実現できることでしょう。

また、コンポーネントの入れ子やCSSのカプセル化にもバッチリ対応しております。

# Canvasとの比較
大きな違いはやはりテンプレートがあるかどうかです。
Canvasでは描写をすべてjsコード側で行わなければいけないので、どうしてもロジックがふくらみがちです。
また、HTML/CSSがわかればSVGは割りとすぐ書けるようになりますが、Canvasはある程度学習が必要でコストになるかもしれません。

他にも、SVGとCanvasではパフォーマンスに差が出ます。
どちらが優秀というわけではないのですが、パーティクル表現など、オブジェクトの数が多い場合にはCanvasのほうがパフォーマンスが良く、大きい解像度での表示にはSVGが強い、という結果になるようです。
![pafformance.png](https://qiita-image-store.s3.amazonaws.com/0/104311/8f48770e-2637-d3d2-61d4-9d1fd81c3dd1.png)
[Microsoft | Developer Network - SVG と Canvas: どちらを選ぶか](https://msdn.microsoft.com/ja-jp/library/gg193983(v=vs.85).aspx#19graphicsInHtml5)

CanvasとSVGの比較については以下のMicrosoftのDeveloper Networkのページで詳細にまとめられておりますので、技術選択に迷ったときには御覧ください。

 - [Microsoft | Developer Network - SVG と Canvas: どちらを選ぶか](https://msdn.microsoft.com/ja-jp/library/gg193983(v=vs.85).aspx)

# AngularでSVGを利用するにあたって、考慮すること
ここまで読んでAngularでSVGを早速使ってみたくなったかもしれませんが、実はSVGをそのままAngular内で使おうとすると、結構壁にぶつかります。

## SVG内の要素には `svg:`を付与すべし
Angularは`<svg></svg>`で囲われた要素を自動的にSVGの要素だと判断しますが、コンポーネントを入れ子にした場合など、未知の要素だと誤判定してしまいうまく動かないことが有るようです。

なので、

```html
<svg>
  <g>
    <rect x="0" y="0" width="60" height="60"/>
  </g>
</svg>
```

のようなコードは、`svg:`プリフィクスをつけてあげましょう。

```html
<svg>
  <svg:g>
    <svg:rect x="0" y="0" width="60" height="60"/>
  </svg:g>
</svg>
```

## 属性バインディングするときは `attr.`を付与すべし
SVG要素はAngular側に属性が定義されておらず、そのまま値をバインディングすることができないようです。 以下のように書きましょう。

```html
<svg>
  <svg:rect [attr.x]="x" attr.y="{{ y }}" attr.class="box {{ cls }}" width="60" height="60"/>
</svg>
```

Angular側で定義している`ngClass`などはそのまま利用できます。

## SVG内にコンポーネントを入れ子にする場合は、属性セレクタなどでコンポーネントを作成すべし
SVGはXMLであるため、svgタグ内に未知の要素を許しません。 そのため、`<svg><my-cmp></my-cmp></svg>`のように利用するコンポーネントはエラーになってしまいます。

以下のようなセレクターのコンポーネントを作成し、`<svg><svg:g my-cmp></svg:g></svg>`のように利用しましょう。

```ts
@Component({
  selector: '[myCmp]',
  templateUrl: './my-cmp.component.html'
})
```

普通はg要素やsvg要素を利用するようです。

# その他・所感
- 今回の記事を書くにあたって、ババっと作ってみたSVGとAngularで制作したインタラクションコンテンツを[Github Pagesに上げました](https://ver-1000000.github.io/piyo-doodle-svg/)。 [ソースコードはこちら](https://github.com/ver-1000000/piyo-doodle-svg)です。
- アニメーションはすべてCSSです。 AngularAnimationとも相性がいいと思うので、試してみたいですね。
  - SVGにはSMILというアニメーション実装があり、結構これが表現豊かなのですが、Chromeが廃止するとかしないとか言ったりしているので、利用しないほうがよいでしょう。
- templateUrlに.svgのファイルを食わせたいのだけれどもangular-cliだけだとできないのだろうか？

# 参考・関連
 - [SVG and Canvas Graphics in Angular 2](https://teropa.info/blog/2016/12/12/graphics-in-angular-2.html)
 - [SVG in Angular 2 - 500 TECH](https://500tech.com/blog/all/svg-in-angular-2/)
 - [Angular2でもとりあえずSVGを描いてみる - Qiita](https://qiita.com/_likr/items/b2cbaa0e5e3e21a92e57)
 - [HTML5でのSVGファイル操作のおさらい - Qiita](https://qiita.com/ka215/items/f9834dca40bb3d7e9c8b)

---

ありがとうございました。 明日は [puku0x](https://qiita.com/puku0x) さんです。