※なお, スクリプトによるアニメーションを行う場合はまた違った結果となる.
経緯
mix-blend-modeを使った合成をfilter要素で書き換えるコードを記述していた際、たまたまuse要素を使うロジックにしたところ、出力したSVGの描画速度に驚くほどの差が発生したことに気づく.それまではpath要素の描画が順次行われており,SVGグラフィックの構築過程が観察できていたのが,ほぼ瞬時にスクリーンに表示されるようになった.(※SVGを直接ブラウザで表示した場合)
考察
canvas要素にも言えることだが,スクリーンの逐次書き換えはコストのかかる処理である.HTMLと同じと仮設を立てると,何の対策も施していないSVGではコードがDOMにパースされた後,DOMツリーの内容に従って順次グラフィックが描かれていくはずだ.サイズの大きいHTMLでは大抵の場合,追加すべきグラフィックはスクリーンの範囲外に追いやられてしまうのでさほど影響を及ぼさないが,SVGにおいては常に単一のスクリーンの再描画を行うこととなり,致命的なパフォーマンスの劣化が発生する.
一方defs要素に定義されたグラフィックをuse要素から参照する場合,一旦defs要素内で解析済みの内容をuse要素部に描画するだけであり,その結果スクリーンの書き換え処理の回数を極小化できる.つまり,オフスクリーンレンダリングのような効果が得られる.
検証
複雑なpathから構成される大きなSVGを、(1)「生DOMが直接描画されるように構成」、(2)「defs要素内の図形をuse要素から参照するように構成」の2つで作り、直接ブラウザで表示する.
その際,SVG読み込み開始時とwindowのloadイベント発生時との間の経過時間を計測する.
構造
いずれも同じ構成でおよそ30MBのSVGファイルとした.
(1) 生DOMが直接描画されるように構成
<svg>
<script>
var start = Date.now();
window.onload = function(){console.log(Date.now() - start);}
</script>
<g><path/><path/><path/>・・・</g>
<g><path/><path/><path/>・・・</g>
<g><path/><path/><path/>・・・</g>
</svg>
(2)defs要素内の図形をuse要素から参照するように構成
<svg>
<script>
var start = Date.now();
window.onload = function(){console.log(Date.now() - start);}
</script>
<defs>
<g id="r"><path/><path/><path/>・・・</g>
<g id="g"><path/><path/><path/>・・・</g>
<g id="b"><path/><path/><path/>・・・</g>
</defs>
<use xlink:href="#r"/>
<use xlink:href="#g"/>
<use xlink:href="#b"/>
</svg>
結果
いずれも10回表示し、その平均をとった結果.
Firefox | Chrome | |||
(1) | (2) | (1) | (2) | |
15984 | 4298 | 989 | 948 | |
15985 | 4250 | 1018 | 958 | |
15407 | 4187 | 842 | 966 | |
16471 | 4162 | 844 | 971 | |
16098 | 4251 | 939 | 964 | |
15420 | 4504 | 793 | 962 | |
15879 | 4386 | 837 | 963 | |
16522 | 4280 | 1317 | 953 | |
15801 | 4385 | 899 | 963 | |
15884 | 4566 | 1064 | 967 | |
平均 | 15945.1 | 4326.9 | 954.2 | 961.5 |
Firefoxでは実に3倍以上の差が発生した.
Chromeではloadイベント発生時とグラフィック完成時との間に差異があるため,有意な差が無いように見える.むしろ(2)のほうがuse要素の分DOM解析に時間がかかっている.が,目視では明らかに(2)のほうが早い.
結論
- SVGを直接ブラウザで表示する(タブ内,iframe内,object内)場合,
図形を直接描画するよりも一旦defs要素で定義し,その内容をuse要素で複写した方が(初期)描画パフォーマンスが上がる.
[条件]
- 比較的多数の図形要素から構成されている.
- 含まれているpath要素の形状が複雑である.
- なおimage要素やimg要素によるレンダリングではまた違った結果となる気がする. (未検証)
- なお,アニメーションを行う場合はこの限りではない.あくまで静的で複雑なグラフィックを表示する際に有効.
まとめ
SVGの動作パフォーマンスを改善させるための優先順位
- DOMノード数を減らす(ノード数≒描画回数)
- 暗黙のレンダリングを回避する(markerの使いすぎ等)
- defs-useを使ったオフスクリーンレンダリングを活用する(New!)→但しこれは静的なグラフィックに限る!
- DOMの編集は最小手数で.一端ノードツリーをDOMから切り離して編集・再挿入.
アニメーション処理で直接プロパティを操作するのは厳禁.
・JavaScript による SVG への実行時描画を高速化する
http://devadjust.exblog.jp/22220156/
での理屈とも合致する.
※innerHTMLによる書き換えはやり過ぎると強烈なdomガベージが発生するのであまり良くない. - パスの複雑性を減らす(曲線部を減らす,精度を落とす)
- 適切なg要素の階層化
0 件のコメント:
コメントを投稿