2012年8月1日水曜日

svgで写真をまき散らしたような効果をつける

http://svg-wow.org/blog/2010/09/18/picture-shuffle/
にインスパイアされてちょっと似たようなコードを自作してみた.因みに元のコードは一切参照していないので,全て当方の自作です.大体同じような形を採っているのじゃないかと(本当?).

ただそのまま真似るのでは芸が無いので,マウスドラッグでフォト部分を移動できるようにしてみた.が,内部的に複雑な構成を採っているためか,動作は極めて鈍重.高スペックなマシンなら幾分かましに動作するかもしれない.




以下コード.パッと見理解しにくい構成だ.というのも,svgの機能をフル活用しているため.maskにfilterにuseにstyleと,あらかたの要素が登場している.

えーと・・・解説するのめどい.
とりあえず,ポイント
  • 写真に相当する図形は一箇所.pieceを使い回している.
  • photoで写真の集まりを表す.g要素が2重になっているのは,内側のg要素でスタイル未指定の矩形群をmask要素に渡すため.こうすると図形の外形と描画内容とを分離することができる.
  • photo内部のuse要素をg要素で囲んでいるのは,filterによる影の生成処理をtransformによる回転処理の後に行うため.
  • photoフレームを描画した上にマスクを掛けた画像を重ねれば最終的なグラフィックが得られる.
  • スクリプト…transform属性をスクリプトから操作するにはmatrixオブジェクトを使うとパフォーマンス上有利と思われる.平行移動するだけなら,e,fプロパティを触るだけだし.
  • use要素におけるイベントを発生するオブジェクトがブラウザによって異なる!
    SVGUseElementだったりSVGElementInstanceだったり.どっちみちuse要素を抽出できるので問題はない.
  • filterを重ね掛けして立体感をUP(単にコーディングミス・・・けど結果オーライ)
こんなところか?

<?xml version="1.0" standalone="no"?>
<svg width="500px" height="500px" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" overflow="visible">
 <style type="text/css">
  #photo>g{
    filter:url(#shadow);
  }
 </style>
 <defs>
  <rect id="piece" width="250" height="180"/>
  <mask id="photoMask" maskUnits="userSpaceOnUse" x="0" y="0" width="500" height="500">
   <use xlink:href="#photo" stroke="black" fill="white"/>
  </mask>
  <filter id="shadow" x="-25%" y="-25%" width="150%" height="150%" filterUnits="userSpaceOnUse">
   <feOffset in="SourceAlpha" dx="2" dy="2" result="alpha"/>
   <feGaussianBlur in="alpha" stdDeviation="1" result="shadow"/>
   <feMerge>
    <feMergeNode in="shadow"/>
    <feMergeNode in="SourceGraphic"/>
   </feMerge>
  </filter>
 </defs>
 <rect width="100%" height="100%" fill="lightgray"/>
 <g stroke="white" filter="url(#shadow)">
  <g id="photo" stroke-width="10">
   <g><use xlink:href="#piece" x="50" y="50" transform="rotate(15,250,250)"/></g>
   <g><use xlink:href="#piece" x="250" y="250" transform="rotate(35,250,250)"/></g>
   <g><use xlink:href="#piece" x="250" y="150" transform="rotate(20,250,250)"/></g>
   <g><use xlink:href="#piece" x="50" y="300" transform="rotate(-13,250,250)"/></g>
  </g>
 </g>
 <image mask="url(#photoMask)" xlink:href="http://www.konami.jp/products/dl_xbox_dracula_hd/images/charlotte.jpg" x="0" y="0" width="500" height="500" preserveAspectRatio="none" pointer-events="none"/>
 <script type="text/javascript"><![CDATA[
window.onload = function(){
 var svg = document.documentElement;
 var photo = document.getElementById("photo");
 var t;
 svg.addEventListener("mousedown", function(e){

  if(t){
   svg.removeChild(t.clone);
   t = undefined;
  }

  var target = e.target;
  var g;
  //firefox
  if(target instanceof SVGUseElement){
   g = target.parentNode;
  //chrome
  }else if(target instanceof SVGElementInstance){
   g = target.correspondingUseElement.parentNode;
  }else{
   return;
  }

  var transforms = g.transform.baseVal;
  var transform;
  if(transforms.numberOfItems > 0){
   transform = transforms.getItem(0);
  }else{
   transform = g.ownerSVGElement.createSVGTransform();
   transforms.appendItem(transform);
  }
  var matrix = transform.matrix;

  var clone = g.cloneNode(true);
  clone.style.setProperty("fill","blue");
  clone.style.setProperty("fill-opacity",0.2);
  svg.appendChild(clone);
  var c_matrix = clone.transform.baseVal.getItem(0).matrix;

  t = {
   matrix: matrix, 
   dx: matrix.e - e.pageX,
   dy: matrix.f - e.pageY,
   clone: clone,
   c_matrix: c_matrix
  };
 }, false);

 svg.addEventListener("mousemove", function(e){
  if(!t){return;};
  var x = e.pageX + t.dx;
  var y = e.pageY + t.dy;
  if(x == 0 && y == 0){return;}
  t.c_matrix.e = x;
  t.c_matrix.f = y;
 }, false);

 svg.addEventListener("mouseup", function(e){
  if(!t){return;};
  var x = e.pageX + t.dx;
  var y = e.pageY + t.dy;
  if(x == 0 && y == 0){return;}
  t.matrix.e = x;
  t.matrix.f = y;
  svg.removeChild(t.clone);
  t = undefined;
 }, false);

};
 ]]></script>
</svg>

0 件のコメント:

コメントを投稿