ROCHAS

Retina対応にSVGを使う方法とリンクを張る時の注意点

Retina対応にSVGを使う方法や、SVGにリンクを張る時の注意点、Illustratorで書き出す時の注意点・軽量化について調べてみました。
こちらの記事は2012/9/24の記事を再編集し、情報を新しくしました。2015A/W年版です。
何気にハマりどころの多いSVG、3年越しにすっきり!

Agenda

  1. SVG「Scalable Vector Graphics」とは何か
  2. SVGが高解像度ディスプレイに対応する仕組み
  3. <object>タグでSVGをPNGでフォールバックして表示する方法
  4. SVGに<a>タグでリンクを張る時の注意点
  5. CSSでリンクをクリッカブルにする
  6. JavaScriptでIE9のポインターをカーソルにする
  7. SVGに直接cursor=‘pointer'を記述してIE9のポインターをカーソルにする
  8. <object>タグで埋め込んだSVGに直接 xlink:href でリンクを張ると死ぬ
  9. inlIne SVGに xlink:href でリンクを張る時の注意点
  10. SVGをIllustratorで書き出す時の注意点
  11. Illustratorで書きだしたSVGを軽量化する

1. SVG「Scalable Vector Graphics」とは何か

通常Webの画像として使用されるGIF、JPEG、PNGは、ピクセルによって画像を描画するビットマップデータであり、1 つの解像度にしか対応できません。高画質にするほどファイルは重くなり、ネットワークに負荷をかけてしまい兼ねません。
一方SVGは、シェイプ、パス、テキスト、フィルター効果を使用して画像を描画するXMLベースのベクターデータであり解像度に依存しません。そのためRetinaディスプレイなど高解像度のデバイスでも最適に表示してくれるという訳です。
また、ユーザー側でブラウザを拡大表示しても画像が劣化しません。 タッチ・ジェスチャーを登載したデバイスが増えれば当然拡大されることも多くなってきます。つまりSVGはPCでも、スマートフォンでも、印刷でも比較的軽量で高品質な画質が得られるのです。
W3CによるScalable Vector Graphics (SVG) 1.1 (Second Edition)に仕様が定義されており、拡張子は.svg、MIMEタイプはimage/svg+xml、ブラウザでサポートしているドキュメントタイプはSVG1.1となっています。ですので、Illustratorで保存する際はSVGプロファイルをSVG1.1に設定します。

2. SVGが高解像度ディスプレイに対応する仕組み

SVGをブラウザで表示する方法はobject embed iframe dataスキーム インラインで記述するなどいくつか方法がありますが、今回は<object>タグで埋め込む方法をご紹介します。<object>タグはSVG非対応ブラウザ向けに代替画像を設定できるため手軽に高解像度ディスプレイに対応にできます。 いわゆる「@2x画像」はRetinaディスプレイにのみ解像度2倍の画像を表示させる仕組であるのに対し、SVGは非対応ブラウザ、つまりIE8以下に対しPNG、JPEG、GIFなど代替画像を表示し、それ以外は解像度に依存しないベクターデータを表示させるという仕組になります。
このように、利用したい機能が対応していない場合に、代替となる機能に切り替える方法をフォールバックといいます。

3. <object>タグでSVGをPNGでフォールバックして表示する方法

<object data="logo.svg" type="image/svg+xml" width="450" height="120">
 <img src="logo.png" alt="logo" width="450" height="120" />
</object>
  • <object>タグのdata属性にSVGのファイルパスを指定し、type属性にMIMEタイプ"image/svg+xml"を指定します。
  • <object>の開始タグと終了タグの間に<img>タグで代替画像を指定します。 これで、IE8以下では"logo.png"がフォールバックされ、それ以外は"logo.svg"が表示されます。

Demo1 | <object>タグでSVGをPNGでフォールバック

SVG対応ブラウザ

4. SVGに<a>タグでリンクを張る時の注意点

画像にリンクを設定することって多いですよね。その際には注意が必要です。問題が2つ発生しますので1つずつ対処してみます。

  • <object>タグで埋め込んだSVGに<a>タグでリンクを設定すると、リンクがクリッカブルにならない。
  • IE9はポインターがカーソルにならない。

Demo2 | <a>タグのリンクがクリッカブルにならない失敗例

5. CSSでリンクをクリッカブルにする

リンクがクリッカブルにならない問題は以下のCSSを書き足すことで対処することができます。

  a {
    display: inline-block;
  }
  object {
    pointer-events: none;
  }
  • object要素はa要素などインライン要素の中で使われた場合はインライン要素として扱われるため、クリッカブルできる高さが確保できていない。
    a要素をdisplay:inline-blockもしくはblockに設定します。
    inline-blockで行内に配置される(前後で改行されない)ブロック要素にすることで、幅や高さの概念を持たせることができます。
  • object要素が親要素であるa要素に覆いかぶさってしまい、クリックイベントを渡してくれない。 object要素をpointer-events: noneに設定します。
    pointer-eventsはどのような状況で要素がポインターイベントのターゲットになれるかを指定するプロパティです。 noneでobject要素のクリックイベントを無効にすることで、親要素であるa要素にイベントを渡すことができます。

Demo3 | CSSでリンクをクリッカブルにする

6. JavaScriptでIE9のポインターをカーソルにする

IE9ではa:hover {cursor: pointer;}にしてもポインタがカーソルにならないため、JavaScriptが必要です。

<style>
a {
  display: inline-block;
}

object {
  pointer-events: none;
}
</style>

<script>
function pointer(){
    var object = document.getElementById('object');
  if (object.contentDocument)
  var svgdoc = object.getSVGDocument();
  var svgelm = svgdoc.documentElement.style.cursor='pointer';
}
</script>
</head>

<body>
<h1>
<a href="http://dresscording.com/svg/retina_svg.html">
<object id="object" onload="pointer()" data="logo.svg" type="image/svg+xml" width="450" height="120">
 <img src="logo.png" width="450" height="120" alt="logo"/>
</object></a>
</h1>

.getSVGDocument()でDOMからSVGドキュメントを取得した上でcursor='pointer'に設定します。 getSVGDocument()はドキュメント内のobject要素を探して外部SVGオブジェクトを取得するメソッドで、onloadイベントに紐付けしないと(ドキュメントがロードされていないと)上手くいきません。(onmouseoverイベントやwindow.onloadだとIE8以下でエラーが起きます?)

Demo4 | CSS+JavaScriptでIE9のポインターをカーソルにする

7. SVGに直接cursor='pointer'を記述してIE9のポインターをカーソルにする

Twitterでまとりさん(@ub_pnr)からアドバイスをいただいたので早速実験してみると・・・ ub_pnr

Demo5 | SVGに直接“cursor: pointer;"を記述してIE9をカーソルにする

SVGファイルに直接を記述しても、ポインター・カーソルになりました!
まとりさんから「状況によりけりといった感じですが、1箇所だけに決めうちするならありかなーと思いました。何箇所かで使いまわしてリンクがなければjsからやるのもいいと思います。 」とのアドバイスもいただきました。ありがとうございます!!

8. <object>タグで埋め込んだSVGに直接 xlink:href でリンクを貼ると死ぬ

SVGには xlink:href で直接リンクを張ることができます。
ところが<object>タグで使用すると、クリックした時にページそのものが埋め込まれた状態になってしまいます。また名前空間であるxmlns:xlinkを省略するとエラーになり表示さえされません。

Demo6 | <object>タグで埋め込んだSVGに直接 xlink:href でリンクを貼ると死ぬ

9. inlIne SVGに xlink:href でリンクを張る時の注意点

というこでinline SVGにxlink:hrefでリンクを張ってみる。もちろん問題ありませんが、注意点としてはパス上しかクリック領域にならないのでfill属性で塗りつぶすなどしておいたほうがよさそうです。

いろいろハマりどころがありますね・・・。
Demo7 | Demo7 | inlineSVGに xlink:href でリンクを貼る xlink:href でリンクを貼る

10. SVGをIllustratorで書き出す時の注意点

Illustratorで作ったベクター画像は「別名で保存」から保存することができます。 ただしここでもいくつか注意点があります。

作画時

  • アートボードの座標(0 , 0)を起点にする。
    適当な位置に作画してしまうとviewBoxとviewPortとズレてしまうため、ブラウザで意図しない位置に表示されてしまいます。viewBox="0 0 450 120"のように左上を起点にしておくとわかりやすい。

  • 軽量化するために非表示のいらないレイヤーは削除する。
    SVGで保存するとレイヤー毎に<g>タグによってグループ化されて書き出されます。非表示のレイヤーも「display="none"」として保持されるためその分記述が増え、データが重くなってしまいす。

  • オブジェクトの変形で移動したり、回転したり、拡大・縮小するとtransform属性が入り、コードが冗長化してしまうことがある。

  • 演算、オーバーレイなどのレイヤー効果、グラデーションメッシュや埋め込み画像は、その部分だけラスタライズされて保存されるので、データが急激に重くなる。

書き出し時

  • CS5までは「Web用に保存」ができたのですがCS6からできなくなりました。そのかわり複数のアートボードを保存できるようになったので、CS6以降で複数の画像を保存したい場合は、スライスではなく「別名で保存」から「アートボードごとに作成」で保存する。 SVGオプション

  • SVGプロファイルは「SVG1.1」に設定。

  • フォントは「アウトラインに変換」するか、フォントとして扱う場合にはCSS側でWebフォントを読み込む。でないとインストールされていないデバイスではデバイスフォントで表示されてしまいます。

Demo8 | アウトラインに変換しないで保存

アウトラインに変換しないで保存

  • CSSプロパティは「プレゼンテーション属性」を設定。CSSとSVGとでコードを分けて使用したい時は「スタイル要素」を設定。

  • 小数点以下の桁数は3くらいに設定。1〜7まで設定でき、高い程ベクトル精度は高くなるが重くなる。

  • エンコーディングは「Uinicode(UTF-8)」

11. Illustratorで書きだしたSVGを軽量化する

これで書き出しは完成!ですがIllustratorで書きだしたSVGは不必要なコードもあるし、まだまだ軽量化したいところ。

<?xml version="1.0" encoding="utf-8"?>
< !-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -- >
< !DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
   y="0px" width="400px" height="400px" viewBox="0 0 400 400" style="enable-background:new 0 0 400 400;" xml:space="preserve">
<path style="fill:#181717;" d="M387.235,186.644L209.893,41.704c-5.76-4.707-14.033-4.707-19.79,0l-45.424,37.132V59.563(略)/>

仕様として最低限必要なコードは

  • svgタグ
  • xmlns属性(HTML5では名前空間はなくてもよい。SVG単体で使う時は必要)
  • width, height属性
  • viewBox属性

なので、こちらのツールで必要なコードだけを抽出し軽量化してしまいます。
SVGOMG | SVGO’s Missing GUI

するとAdobeでジェネレイトした旨のコメントアウトや、ドキュメントタイプ、名前空間が削除され、約24.7%軽量化!
色々設定も可能で、<g>タグも削除でき、<path>タグも1つにまとまりました。

SVGOMG | SVGO's Missing GUI

この記事を書いたのは2012/9/24、今ほど情報もない中試行錯誤していましたねw
今ならSVGはinlineで、書き出しはSketchで・・・と思ったり。あ、でもIllustratorもCC2015からかな、だいぶ無駄なコードが減りましたよ!
とにかく仕様を見直すいい機会になってよかったです。

参考サイト