2012年9月20日木曜日

svgを使って図案とレイアウトとを分離する

svgは画像の表現形式であると同時にグラフィックを描くためのプログラム言語とも言えます.そのために,svgを手書きする場合に便利な機能が沢山定義されており,上手く組み合わせることで煩雑な作業を大幅に簡略化すると言ったことも可能です.ここではuse要素とスタイルシートとを組み合わせて基本となる図案とレイアウトとを分離し冗長なコードを省く方法について紹介します.

例えばトランプを思い浮かべて下さい.トランプのカードには4種のスート(マーク)と13種の位が存在しており,全部で13×4の52種類のカードが存在します.ここで同じ位のカードはマークが異なるだけで同じレイアウトとなっている点に着目します.

素朴に考えると,全部で52種類のカードをデザインすれば良いのですが,同じレイアウトなのだから何らかの方法で作業を簡略化したいところです.そこでsvgの機能を使ってスートの部分とレイアウトの部分を分離し,あとから動的に組み合わせることができないか?というのが今回の着想の原点です.

実現したい機能は次の通りです.
  • 52種類のカードを一つのファイルにまとめたい.
  • スートとレイアウトとを分離し,後から簡単にレイアウトを変更可能としたい.
  • svgを使う場合は52種類のカードの中から一つのカードを選択可能としたい.
では試してみましょう.4つの画像が全て同じファイルを参照している点に注意して下さい.


カードの選択は「cards.svg#h1」と言った風に行います.
動作はfirefox,chrome,operaで確認しています.
追記)52枚+ジョーカー+裏面に対応させました.内容を編集するなりいろいろと試してみて下さい.

以下にコードを示します.
<?xml version="1.0" standalone="no"?>
<svg width="200px" height="300px" viewBox="0 0 200 300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
 <style>
/*スートの選択(chrome,opera)*/
.spade:target~defs #spade{
 display:inline;
}
.hart:target~defs #hart{
 display:inline;
}

/*スートの選択(firefox)*/
.spade:target~.card #spade{
 display:inline;
}
.hart:target~.card #hart{
 display:inline;
}

/*カードの選択*/
.ace:target~#ace{
 display:inline;
}
.two:target~#two{
 display:inline;
}
 </style>
 <!--カード選択用のダミー要素-->
 <rect id="s1" class="spade ace"/>
 <rect id="h1" class="hart ace"/>
 <rect id="s2" class="spade two"/>
 <rect id="h2" class="hart two"/>

 <!--カードの表面-->
 <rect x="5" y="5" width="190" height="290" rx="10" ry="10" stroke="black" fill="white"/>

 <!--レイアウト-->
 <g id="ace" class="card" display="none">
  <use xlink:href="#sute" y="50" width="200" height="200"/>
 </g>
 <g id="two" class="card" display="none">
  <use xlink:href="#sute" x="50" y="25" width="100" height="100"/>
  <use xlink:href="#sute" x="50" y="150" width="100" height="100"/>
 </g>

 <!--シンボル-->
 <defs>
  <symbol id="sute" viewBox="0 0 100 100">
   <path display="none" id="spade" d="m 35.848886,93.252387 c 2.417956,-6.2823 4.5221,-12.7304 …" fill="black"/>
   <path display="none" id="hart" d="M 51.336451,91.104936 C 40.659695,82.468763 …" fill="red"/>
  </symbol>
  
 </defs>
</svg>

ポイントは:target擬似クラスを使って重なりあったスートとレイアウトの中から条件に合致したものだけを表示する点です.4つのスート及び13枚のカード(今回は技術検証のため2スート2レイアウトとなっている)を予め重ねておき,target擬似クラスを使ってそれぞれ一つだけ取り出して1枚のカードを構成しています.

targetとなるダミー要素を52枚分用意しておき,間接セレクタ「~」を使って実際のカードを構成する要素のdisplay属性を書き換えています.

注意する点として,firefoxとchrome/operaとでスートの抽出方法が異なることが挙げられます.前回示しましたが,firefoxはuse要素は参照先の要素のクローンを生成してしまうため,use要素の配下に図形要素が存在しているものとしてスタイルを指定する必要があるのです.

このようにsvgはillustrator等のグラフィックツールを使うだけでは気付きにくい機能が存在しており,css等の仕組みと組み合わせることで実に様々なことを実現することができます.中々難しいとは思いますが試してみる価値は十分にあると思います.

0 件のコメント:

コメントを投稿