2.IDEA

JP only

SVGOを使ったSVGの軽量化方法(アニメーションさせるときの注意とか)

SVGは、どんなデバイスでも綺麗に見える上、フォールバックさえ用意すればIE等のブラウザにも対応できて大変便利です。

しかし、便利な反面、使う際にはいくつか注意すべきことがあります。そのひとつがパフォーマンスです。

PNG等のピクセルごとに色を表現する形式のビットマップ画像とは違い、ベクター画像はブラウザに「これこれこういう風に描画して」と「書き方」を命令する形式です。つまり、同じ見た目を表現するにも、いろいろな書き方が存在します。

そのため、コード量を多くしようと思えばいくらでも多く出来てしまい、結果としてかなりのパフォーマンスを損なってしまいます。

そこで今回は、SVGOによるSVGの軽量化方法や、SVGOをSVGアニメーションに使う場合の注意点を、先日私がMashup Award 10で花王賞を頂いたSVGのスクロールアニメーションを使った作品の軽量化で得た知見と共に、説明していきたいと思います。

※PCのみ、モダンブラウザのみにしか対応していません。

花王のSVGアニメーション作品のスクリーンショット画像

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を使いこなすときではないでしょうか。カレンダーまだまだ空いていますので、是非参加してみて下さい。

SVG Advent Calendar 2014、次の日の記事は@rikuoさんの「SVGOの最適化で困ること、そして複合パス」です!

おまけ

実はcleanupListOfValuesというプラグインは私が2日前に作ったものでした。

polygonを使ったモーフィングを作ったときに、SVGOではpointsの座標の小数点を削れないことに気づいて、よく見ると複数の値をもつ属性(viewBox, enable-backgroundなど)は全部小数点を削れないことに気がつきました。

なので、自分で作って、おそるおそるプルリクエストを送ってみたら、マージして頂きました。
日本人特有の無駄な謙虚さでデフォルトはfalseにしていますが、悪いことは起こらないので皆さん是非trueにして使って、どうぞ。