SVGは、どんなデバイスでも綺麗に見える上、フォールバックさえ用意すればIE等のブラウザにも対応できて大変便利です。
しかし、便利な反面、使う際にはいくつか注意すべきことがあります。そのひとつがパフォーマンスです。
PNG等のピクセルごとに色を表現する形式のビットマップ画像とは違い、ベクター画像はブラウザに「これこれこういう風に描画して」と「書き方」を命令する形式です。つまり、同じ見た目を表現するにも、いろいろな書き方が存在します。
そのため、コード量を多くしようと思えばいくらでも多く出来てしまい、結果としてかなりのパフォーマンスを損なってしまいます。
そこで今回は、SVGOによるSVGの軽量化方法や、SVGOをSVGアニメーションに使う場合の注意点を、先日私がMashup Award 10で花王賞を頂いたSVGのスクロールアニメーションを使った作品の軽量化で得た知見と共に、説明していきたいと思います。
※PCのみ、モダンブラウザのみにしか対応していません。
SVGOの使い方
SVGOの使い方は簡単なので、公式のREADMEを見ればわかるかと思います。
SVGOをインストールして、コンソールで実行すればオッケーです。(npmコマンドが使えない方は、まずお使いのパソコンにnode.jsをインストールしてください)
$ [sudo] npm install -g svgo
$ svgo test.svg test.min.svg
これでも軽量化は出来るんですが、設定がデフォルトのままです。
デフォルトの設定では、軽量化として不十分な部分や、逆にアニメーションに使うにはやり過ぎな部分があるので、自分で設定ファイルを用意する必要があります。
設定の方法
軽量化の設定をしていくんですが、SVGOはそもそもJavaScriptプラグインの集合です。複数のプラグインをまとめて実行しているだけです。
設定ファイル(YAMLファイル)では、どのプラグインを実行してどのプラグインを実行しないかを決めることができます。
YAMLファイルの用意
デフォルトの設定ファイルは、グローバルインストールしたnode_modulesの中に入っているはずです(以下は私のMacbookでの場所です)。
/usr/local/lib/node_modules/svgo/.svgo.yml
このファイルを直接いじっても設定は変更できるのですが、自分で設定ファイルをつくるのが正攻法なので作ってみます。
といっても、デフォルトのファイル(さっきの.svgo.yml)をコピペして、プラグイン名のところを使うならtrue、使わないならfalseにするだけです(プラグインによって、デフォルトでtrueだったりfalseだったりします。あとで紹介します)。
- [プラグイン名]: true/false
ブラグインによっては詳細なパラメータの設定ができます(パラメータを設定すると自動的にtrueになるので注意しましょう)。
- [プラグイン名]:
[詳細なパラメータ]: [値]
設定が終わったら、実行時に--configでYAMLファイルのパスを指定してあげればオッケーです(以下はSVGファイルとYMLファイルが同じ場所にある場合です)。
$ svgo --config=mySVGO.yml test.svg test.min.svg
よく使う設定
デフォルトでは「小数点以下を第何位まで残すか」の設定が小数第三位までになっているので、以下のパラメータ設定は必ずと言っていいほど使うかと思います(以下の設定では小数第一位まで残すようにしています)。
- convertPathData:
floatPrecision: 1
- convertTransform:
floatPrecision: 1
- cleanupNumericValues:
floatPrecision: 1
- cleanupListOfValues:
floatPrecision: 1
大体の場合、「小数点以下1桁まで」で良いかと思いますが、細部の表現が必要なときは調整が必要です。これについては、SVG Advent Calendar 2014の3日目にrikuoさんが詳しく丁寧に説明されているので、是非参照下さい。
アニメーションを使わないSVGであれば、アクセシビリティを損なう部分だけfalseにして(デフォルトでfalseになっています)、他すべてをtrueにした以下のようなファイルを使うと良いかと思います(transformsWithOnePathも不要なのでデフォルト(false)のままにしています)。
プラグインの紹介
では実際にどういうプラグインが入っているのか説明します。デフォルトではtrueなのかfalseなのか、というのもこちらで確認できます。
「ほとんどの場合でtrueで問題ないプラグイン」と「使うときは注意した方がいいかもしれないプラグイン」に分けました。
どういう風にコードを短くしているのか、何故短くしていいのかなどは、昨年のGraphical Web Advent Calendar 2013のrikuoさんのSVG軽量化についての記事でとてもわかりやすく説明されているので、是非参照下さい。
ほとんどの場合でtrueで問題ないプラグイン
プラグイン名 | 説明 | デフォルト |
---|---|---|
removeDoctype | DOCTYPE宣言の削除 | true |
removeXMLProcInst | XML宣言の削除 | true |
removeComments | コメントの削除 | true |
removeEditorsNSData | エディタ(Illustrator, Sketchなど)固有の余計な名前空間の削除 | true |
cleanupAttrs | 属性の値の中の余計な空白を削除 | true |
convertStyleToAttrs | styleで指定しているものを属性に変換(style=”fill:#111” → fill=”#111”) | true |
removeRasterImages | SVG内に埋め込まれた画像を削除 | false |
cleanupNumericValues | widthやx、yなど、1つの値をもつ属性の値を短くする(小数点以下の繰り上げ、pxの削除など) | true |
cleanupListOfValues | pointsやviewBox、enable-backgroundなど、複数の値をもつ属性の値を短くする(小数点以下の繰り上げ、pxの削除など) | false |
convertColors | rgb, hex, カラーネーム("red"とか)の中で、一番短くなる記述方法に変換(#f00はredに変換されますが、#000はblackの方が長いのでそのままです) | true |
removeNonInheritableGroupAttrs | 仕様上、<g>に指定した値がその子要素に引き継がれない属性(displayとopacityは除く)を削除(仕様書を見ましたが、実用の際にはほとんど使われない属性ばかりです) | true |
cleanupEnableBackground | エディタ(Illustrator, Sketchなど)が勝手に付ける余計なenable-bakcgroundを削除(filterのためにあえて使っているものは消えません) | true |
removeEmptyText | 文字が空の<text>要素を削除 | true |
moveElemsAttrsToGroup | <g>でまとめた要素すべてに共通する属性を、<g>で一括指定 | true |
moveGroupAttrsToElems | <g>とその子孫の図形要素にtransformが別々に指定されていたら、図形要素の方で一括指定 | true |
convertTransform | transformの値を短くする | true |
removeEmptyAttrs | 値が空の属性を削除 | true |
removeEmptyContainers | 空のコンテナを削除(<defs></defs>など) | true |
removeUnusedNS | 使っていない名前空間を削除(<svg>のxmlns="http://www.w3.org/2000/svg"は残します) | true |
sortAttrs | 属性の並び順を統一 | false |
使うときは注意した方がいいかもしれないプラグイン
プラグイン名 | 説明 | デフォルト |
---|---|---|
removeMetadata | <metadata>の削除 | true |
removeUnknownsAndDefaults | SVGの属性ではない属性(data属性など)や、SVGの要素ではない要素(<div>など)を削除。<svg>要素のidやversionも削除。 | true |
removeUselessStrokeAndFill | 無意味なstrokeやfillの属性を削除。(stroke-width:0やstroke-opacity:0の場合はstrokeを削除など) | false |
removeViewBox | viewBoxの削除(viewBox="0 0 (width) (height)"の場合のみ) | false |
removeHiddenElems | 見えない要素を削除(opacity: 0, width: 0, display: noneなどが指定されたもの) | true |
convertShapeToPath | コードが短くなる場合だけ、<line>, <rect>, <polyline>, <polygon>を<path>へ変換 | true |
collapseGroups | 重複した<g>や不要な<g>を結合したり削除したりする | true |
convertPathData | <path>のコードを全力で短くする(SVGOの醍醐味はこのプラグインです) | true |
mergePaths | 結合できる<path>を結合する | true |
cleanupIDs | SVG内に<style>や<script>がなければidを削除。idにアンカーが貼られていたら削除せずにid名を縮小(a,b,c...とアルファベット順に割り振られます) | true |
transformsWithOnePath | - | false |
removeTitle | <title>の削除 | false |
removeDesc | <desc>の削除 | false |
注意すべきプラグインの注意した方が良いとき
- ・removeMetadata
- metadataを使ってSVGをよりアクセシブルにできるので、その用途で使用している場合はfalseにします。
- ・removeUnknownsAndDefaults
- data属性を使うときや、SVGの中に<div>などを書くときにはfalseにします。
- ・removeUselessStrokeAndFill, removeHiddenElems
- 最初はopacity: 0でフェードインするときなどはfalseにします。
- ・removeViewBox
- viewBoxは指定したほうが安全です。
- ・convertShapeToPath
- その図形要素特有の属性でアニメーションを設定している場合(<rect>のxを動かす等)は、<path>に変換してしまうと動かないのでfalseにします。
- ・collapseGroups
- アニメーションをさせるまとまりとして<g>を使うときは、falseにします。
- ・convertPathData
- 採用はすべきですが、詳細設定に気を使う必要があります。
- ・mergePaths
- 部分ごとに分離してアニメーションさせるためにあえてパスを結合しなかったとき、結合しないほうが点数が少なくなる場合はfalseにします。
- ・cleanupIDs
- JavaScriptで要素を取得するためにidを指定するので、アニメーションでは基本的にオフにします。
- ・transformsWithOnePath
- ※convertPathDataに統合されたので不要です。transformを指定した<path>を、transformを使わない新しい<path>に変換してくれます。
- ・removeTitle, removeDesc
- アクセシブルでなくなるので基本はfalseかと思います。詳しくはSVG を始めるためのベスト プラクティスのアクセシビリティの項を参照ください。
※ちなみに、これらの挙動を自分の目で確かめたい場合は、githubでユニットテスト用のSVGファイルが用意されていて、プラグインごとに実行前と実行後でどう変わるかが見れます。
軽量化の実践
説明が終わったので、実際に軽量化を始めていきます。パーツごとに軽量化しました。基本設定は以下みたいになりました。
気をつけた点は以下です。
- JavaScriptでいじるのでidは残す
- skrollr(jQueryプラグイン)を使うためdata属性を残す
- 小数点以下は小数第一位まで残す
- 部分部分でアニメーションをして欲しいので、パスの結合はしない
- cleanupIDs: false
- removeUnknownsAndDefaults:
unknownAttrs: false
- convertPathData:
floatPrecision: 1
- convertTransform:
floatPrecision: 1
- cleanupNumericValues:
floatPrecision: 1
- cleanupListOfValues:
floatPrecision: 1
- mergePath: false
では、パーツごとに見ていきます。
花王のロゴの部分
ロゴには花王サイトへのリンクが貼ってありますが、単純にpathにリンクを貼ると、描画されている部分にしかリンクが貼られません。なので、 透明な四角形を描いてクリック領域を確保しています。
しかし、デフォルトの設定で軽量化を走らせると、この透明な四角形が消えてしまうので、removeHiddenElemsをfalseにしました。
アニメーションはしないのでパスは結合して、小数点の設定は、ロゴは会社にとって重要な要素でその細部の表現を損なうわけにはいかないので、小数点第2位まで残しています。
- removeHiddenElems: false
- convertPathData:
floatPrecision: 2
- mergePath: true
時計の部分
どうやって実装したか忘れてしまいましたが、g要素をまとめるとrotateが効かなくなったので、collapseGroups: falseにしました。
また、今回、skrollrというプラグインを使うためにdata属性を使っていたので、消えないようにremoveUnknownsAndDefaultsのunknownAttrをfalseにしました。
- collapseGroups: false
- removeUnknownsAndDefaults:
unknownAttrs: false
商品の部分
どの商品も基本設定のまま軽量化しました。しかし今見てみると、キュキュットの細い文字の部分がちょっと崩れてしまっているので、小数点の設定は小数点2位までにすべきでした。
あと、地味にエディタが勝手に付けてきたstroke-miterlimitは、手動でsublimeの一括選択で削除しました。
まとめ
最終的に、htmlファイルで見て36.4%くらいカットできました。ただ、data属性のコード量が膨大なせいでまったくスッキリ見えませんね。。
SVGを作成する段階で軽量なSVGにしようと努力することもできますが、「誰がどんなツールを使って作られたSVGでも、あるところまでは軽量化できる」というメリットはやはり大きいと思います。
そもそも、わざわざここまで調べたのは、Sketchで「小数点以下どこまで残すか」の設定ができなかったからです(私が調べた限りですが)。
デバイスのスクリーン解像度が次々に上がる今こそ、SVGを使いこなすときではないでしょうか。カレンダーまだまだ空いていますので、是非参加してみて下さい。
おまけ
実はcleanupListOfValuesというプラグインは私が2日前に作ったものでした。
polygonを使ったモーフィングを作ったときに、SVGOではpointsの座標の小数点を削れないことに気づいて、よく見ると複数の値をもつ属性(viewBox, enable-backgroundなど)は全部小数点を削れないことに気がつきました。
なので、自分で作って、おそるおそるプルリクエストを送ってみたら、マージして頂きました。
日本人特有の無駄な謙虚さでデフォルトはfalseにしていますが、悪いことは起こらないので皆さん是非trueにして使って、どうぞ。