2012年7月23日月曜日

use要素のススメ:svgによる図形のテンプレートの利用

なんかchromeでのuse要素の動作がバグったか?http://stackoverflow.com/questions/11514248/svg-use-elements-in-chrome-not-displayed
以前まで動作していたはずなのにー


7/24更新.インラインsvgの色とテキストの色を一括で指定できることを発見.(確かに仕様ではそう書いてあったなぁ)

SVGよりアイコン・フォント!な理由を読んでいて,ちょっと気になったので考察.
  • SVGでも複数の画像をまとめるまでは簡単に実現できる。ただ画像の切り替えはちょっとわかりにくい。今は:target擬似クラスを使った切り替えしか発明されていないのでこれを使うことになるわけだけど、これに慣れるのは少し時間がかかると思う。そもそも動かないブラウザーも多い。
これはアプローチがちょっと不味いのだと思う.確かにbackground-imageにsvgを使う場合はそうかもしれないが,インラインsvgでuse要素を使って外部svgを読みこめば似たようなことは実現できる.


例を示そう.テンプレートとなるsvgファイルはこちら


ポイントは次の通り.
  • テンプレートとなるsymbol要素を定義するsvgファイルではfillスタイルを指定しない.
    図形の外形のみを定義するようにする.さもないと図形を使う側で色を変更することができない.(ツールで図形を作ると勝手にfill属性を定義してしまうので失敗する.)
  • テンプレートとなるsvgファイルはhtmlと同じ階層に保存する.
    クロスドメインでの読込は失敗する.
  • htmlファイルにsvg要素記述して,その中にuse要素を定義する.
    use要素の参照先を上でのsvg文書の中のsymbol要素とする.
    例)xlink:href="icons.svg#star"
  • 塗り潰しのスタイル付けはhtml上でuse要素に対して行う.
    cssからも,属性を直接指定することも可能.グラデーションを掛けることも可能.
  • また,塗り潰しのスタイルにcurrentColorを指定すれば,htmlの文字列の色を引き継ぐこともできる
    わざわざhtmlとsvgの両方にスタイルを指定する必要はないので,使い勝手はアイコンフォントを使った場合とほとんど変わらない筈.
この動作がもともとの仕様が想定しているsvgのテンプレートとしての使い途だと思う.よって基本的には環境を選ばず利用できるはず.実際firefoxでもoperaでも動作する.(chromeで動作しないのは多分バグ.というのも正常にこれまで動いていたから).

図形の切り替えも単一のsvg要素に複数のuse要素を定義しておき,スタイルシートでdisplay属性を切り替えれば何とかなる.
という訳で,svgで出来ないってことはない.構造も理解してしまえば非常にシンプルだ.
※ここにたどり着くまで相当ハマったが…


それではアイコンフォントは利用すべきではないのか?

個人的な見解では一部を除いて「そのとおり」 だとおもう.誤ったアイコンフォントの利用が広まってしまうのは非常に危険だと思う.

ここでの誤った利用とはcssの「@font-face」を使ってフォントを直接htmlに適用する使い方を指す.アイコンフォント自体はかなり昔から存在していて,フォントの持つ拡大・縮小に強いと言った特徴からクリップアートのような使われ方をしてきた.従ってこの用途がwebに適用可能となっただけとも言えるのだが,ここに落とし穴がある.

例としてhttp://hail2u.net/pub/test/417.htmlを挙げたい.このようにフォントの文字コードが持つ意味をデザインのためだけにねじ曲げている事になるため,一般的なhtml文書で用いると見た目と内容との間に差異が発生してしまうのだ.

これは例えば検索エンジンのインデックス生成に悪影響を及ぼす等,隠れた問題が多い.アイコンフォントはパッと見凄く便利なのだが,インターネット上等のパブリックな環境では副次的にどのような問題が発生するかわからないという点で諸刃の剣だと思う.



唯一影響がないのが,cssの:before/:after擬似要素内での利用であり,この方法であればhtmlのもつ正しさを保ちつつ,見た目だけを変更できる.従って,アイコンフォントを直接利用する場合はこの範囲でのみ利用すべきだと思う.

一方svgはそれが何らかの図形を表しているということが明確なので,アイコン用途等に積極的に利用していける.更にアイコンフォントでの図形の区別が意味のない文字コードで管理されるの対し,svgではより明確なid属性として表すことができるのでメンテナンス性が高い.確かにwebフォントとしての軽快さは無いかもしれないが,svgにも理屈上のメリットは存在する.

[結論]htmlに適用する場合はちょっと待って.安易に使うものじゃないよ. 

2012/10/3追記
unicodeの私用領域をアイコンとして利用する分には問題が出ないので,
[結論その2]アイコンフォントを使うのであれば私用領域にアイコンを設定するといいよ.


  • 参照元のhtmlファイル
    use要素にスタイルを設定することで後から自由に色を変更できる.
<!DOCTYPE html>
<html>
 <head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
 <style type="text/css">
use{
 fill:orange;
}
 </style>
 </head>
 <body>
<style>
svg.icon{
 width:5em;height:5em;display:inline-block;
}
svg.fontLike{
 width:1em;height:1em;display:inline;line-height:1em;
}

</style>
 <svg class="icon"><use width="100%" height="100%" xlink:href="./icons.svg#circle"/></svg>
 <svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#square"/></svg>
 <svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#triangle"/></svg>
 <svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#star"/></svg>
 <svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#star" style="fill:green"/></svg>
 <svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#star" style="fill:red"/></svg>
 <svg class="icon"><use width="100%" height="100%" xlink:href="icons.svg#star" style="fill:blue"/></svg>
 <div style="color:red;">
  文字列とsvg
  <svg class="fontLike">
   <use width="100%" height="100%" xlink:href="icons.svg#star" style="fill:currentColor"/>
  </svg>
  画像との色を一括で設定することができます.</div>
 </body>
</html>
  • 参照先のsvgファイル
    symbol要素だけを定義している.symbol要素はあとからサイズを自由に変更できるので便利.
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
 <symbol id="circle" viewBox="0 0 100 100" preserveAspectRatio="none">
  <circle cx="50" cy="50" r="40"/>
 </symbol>
 <symbol id="circle-hollowed" viewBox="0 0 100 100" preserveAspectRatio="none">
  <path d="M10,50 A40,40 0 0,0 90,50 A40,40 0 0,0 10,50 Z M30,50 A20,20 0 0,0 70,50 A20,20 0 0,0 30,50 Z" fill-rule="evenodd"/>
 </symbol>
 <symbol id="square" viewBox="0 0 100 100" preserveAspectRatio="none">
  <rect x="10" y="10" width="80" height="80"/>
 </symbol>
 <symbol id="rect" viewBox="0 0 100 100" preserveAspectRatio="none">
  <rect x="10" y="15" width="80" height="70"/>
 </symbol>
 <symbol id="triangle" viewBox="0 0 100 100" preserveAspectRatio="none">
  <polygon points="50,15 10,85 90,85"/>
 </symbol>
 <symbol id="moon" viewBox="0 0 100 100" preserveAspectRatio="none">
  <path d="M 20,20 A 35,35 0 1,1 20,80 A 20,20 0 1,0 20,20 Z"/>
 </symbol>
 <symbol id="pentagon" viewBox="-50 -50 100 100" preserveAspectRatio="none">
  <polygon points="0,-40 -38,-12 -24,32 24,32 38,-12"/>
 </symbol>
 <symbol id="hexagon" viewBox="-50 -50 100 100" preserveAspectRatio="none">
  <polygon points="0,-40 -35,-20 -35,20 0,40 35,20 35,-20"/>
 </symbol>
 <symbol id="star" viewBox="-50 -50 100 100" preserveAspectRatio="none">
  <polygon points="0,-40 -24,32 38,-12 -38,-12 24,32"/>
 </symbol>
 <symbol id="star-nonfill" viewBox="-50 -50 100 100" preserveAspectRatio="none">
  <polygon points="0,-40 -24,32 38,-12 -38,-12 24,32" fill-rule="evenodd"/>
 </symbol>
</svg>

もうひとつの方法としてview要素を使った方法があるかもしれない.これならbackground-imageでも動作するかもしれない.(だが,クロスブラウザの部分がちょっと不安)

0 件のコメント:

コメントを投稿