2012年9月19日水曜日

svgにおけるuse要素の動作について

svgにはhtmlに存在しない魅力的な機能が定義されており,様々な画像を作り出すことができますが,現在ブラウザ毎の実装状況が異なるために細かいながらも多くの動作の違いが存在しています.今回紹介するuse要素もその一つで,非常に使い勝手の良い要素であるだけにこの非互換性は頭の痛い問題です.

use要素は既存の図形要素を複写する機能をもちます.単一の図形を何度も使いまわすことができるのでsvgを手書きする場合や,図形をテンプレート化する際に非常に重宝する要素です.

ここで「図形を複写する」とは一体どのような仕組みで行われているのでしょうか?色々とsvgを弄ってみて判ったこととして概ね次の2つの実装に分けられるようです.(なおいずれも推測の域は出ません.)
  1. 元となる図形要素を実際にコピーする.つまりdomオブジェクトのcloneを作る.
    →firefox
  2. 元となる図形要素を参照するプロキシオブジェクトを生成する.
    →chrome,opera

この実装の違いにより様々な動作の違いが発生します.
  • firefoxではuse要素でコピーした要素にスタイルを指定することができてしまう.
    通常use要素の子要素としては図形要素は存在しないはずなのに,あたかも参照先の図形がuse要素の配下に存在するかのようにcssが動作してしまいます.
    例)use rect{fill:red}
    つまり内部的にcloneを生成している事になります.
    chrome,operaではuse要素の内部に存在するのは元となる図形への参照情報だけなので,スタイルを指定することができません.
  • firefoxにおいてuse要素からsmilアニメーションが設定されている要素を参照した場合,そのアニメーションがxlink:href属性によって外側から定義されていた場合,use要素側のアニメーションは動作しない.
    これは要素のクローンを作ったためにアニメーション対象から外れたものと考えられます.
    chrome,operaではプロキシオブジェクトを経由してアニメーションを描画するので,use要素側でもアニメーションが実行されます.
  • イベントを発生する主体が異なる.
    svg要素で子要素から浮上してきたイベントを確認すると,イベントを発生した主体がfirefoxではSVGUseElementが,chromeとoperaではSVGElementInstanceが得られます.
この他にもuse要素を多段構成(use要素がuse要素を参照する)とした際にset要素によるxlink:href属性の設定が上手く行かない等,本来こうあって欲しい部分が実際に試してみると動作しないと言ったことがよく有ります.複雑な構成を取る場合は十分に動作を確認したほうが良いでしょう.

特にuse要素とanimate要素とは相性が良くないようです.smilだけではどうしても上手く行かない場合はjavascriptを使ってdomを操作することを検討しましょう.domによるアニメーションは経験上素直に動作することが多いです.

サンプル
<script type="text/javascript">
 window.addEventListener("load", function(){
  var svg = document.querySelector("svg:last-child");
  var result = document.querySelector("#result");
  svg.addEventListener("click", function(e){
   result.innerHTML = e.target;
  }, false);
 }, false);
</script>
<p>スタイルの適用</p>
<svg id="svg1">
 <defs>
  <rect id="rect1" width="100%" height="100%"/>
 </defs>
 <use xlink:href="#rect1"/>
</svg>
<p>アニメーション設定済み要素の参照</p>
<svg viewBox="-100,-100,200,200">
 <defs>
  <rect id="rect2" x="-100" y="-100" width="100%" height="100%"/>
  <animateTransform xlink:href="#rect2" attributeName="transform" type="rotate" from="0,0,0" to="360,0,0"
   begin="0s" dur="6s" repeatCount="indefinite"/>
 </defs>
 <use xlink:href="#rect2"/>
</svg>
<p>イベント発生の主体:<span id="result"></span></p>
<svg id="svg3">
 <defs>
  <g id="click">
   <rect width="100%" height="100%"/>
   <text y="50" fill="white" font-size="25" font-family="sans-serif">click me!</text>
  </g>
 </defs>
 <use xlink:href="#click"/>
</svg>

0 件のコメント:

コメントを投稿