マップ閲覧・編集システムで画面上に DIV で描いていた図形を SVG に移行した。内部に数百のオブジェクトを含む要素の動作が重く、ドラッグしたときにカクカクになる問題を解決したかった。ちなみに Virtual DOM は使っているがすでに最適化は済んでおり、毎回描画されるような事態はあらかじめ避けている。代替案として Canvas と迷ったが CreateJS のようなライブラリを使わないとイベントハンドリングがきついので、 SVG で行けるならそれが一番。以下、移行した結果と移行中に気付いたことのまとめ。
- 描画パフォーマンスが上がった(Firefox: 20fps ⇒ 60fps、Chrome: 50fps ⇒ 60fps)。
- Chrome の DevTools で見ると、DIV 版は UpdateLayout と Paint に時間がかかっているが、 SVG は Layout に時間がかかっている。
- requestAnimationFrame で測るとデータが飛び飛びだったので平滑化したところ読みやすくなった。
- Virtual DOM も特に問題なく使える(今回は Elm の SVG ライブラリを使用)。
createElementNS
、setAttributeNS
を使わないとSVG要素として動かないので注意が必要。z-index
がない。あらかじめ並び替えておく必要がある。- 隣の要素とボーダーが重なる。普通の DIV だと 1px のボーダーが隣と合わせて 2px になることがあった。
<img>
の代わりに<image>
を使う。title
はあるがalt
はない。 SEO 的には微妙かもしれない。- 相対位置は
<g transform="translate(x,y)">
を使う。x
とy
ではない。 align: center
の代わりにtext-anchor: middle
と 親要素の中央の X 座標を使う。alignmentBaseline
を<g>
などに設置すると Chrome は効くが Firefox では効かないので<text>
に直接つける。- Twemoji のような HTML を書き換えて何かするライブラリが動かなくなる。仕方がないのでいったん仮の要素を作って適用してから、SVG に移し替えた。 SVG の中に HTML を埋め込むことはできる(
foreignObject
要素)ようだが積極活用は避けたい。 viewBox
でマップの平行移動、拡大・縮小などができるが、transition
ができない(頑張ってanimate
で対応しても動作が重い)ので普通に CSS で位置と大きさを指定した方が良い。word-break
のような便利なものはない。foreignObject
要素でも出来るようだが、今回は<canvas>
要素のmeasureText
を使って自力で計算した。
SVG の特性上、やはり文書よりも図形っぽいものに使うのが適していると思う。パフォーマンスは条件によると思うが、少なくとも試す価値はある。Canvas vs SVG は良く見るが、 DIV vs SVG はあまり見なかったので速くなるケースがあることを確認できてよかった。ちなみにデータは以下。計測方法が微妙で発表できる形にまとまっていないので、雑なメモと思って欲しい。元のデータが CSS in JS(Elm) だったので、本当は普通に CSS を使った時と比較した方が良いのだが、その余裕はなかった。
Rendering Performance · Issue #31 · WorksApplications/office-maker · GitHub