2012年4月16日月曜日

svgを使って時計を実装してみよう

例によって時計を作ってみる第3弾.これまで作ってみたHTML要素を用いたもの,canvasを用いたものと比較して非常に簡潔に記述できる印象だ.それはそのとおりで,svgにおいてはhtmlのように文書構造が維持される上,スタイルによる文書構造と見た目の分離を行うことが可能.更にアニメーション要素のお陰で,スクリプトによる時系列的変化の記述をしなくても済むといったメリットがある.

それではcanvas要素を用いた場合はどうかというと,あらゆる処理がスクリプト内部に埋め込まれてしまうため,構造・見た目・動きといった個々の要素が一緒くたになりやすい.よって構造がはっきりしている時計といったものを実現する上では若干使いにくい.もちろんcanvas要素のもつ自由度は特筆すべき点であるため,どちらが優れているといったものではなく単に用途が異なるだけだ.
  • HTMLにおけるhover処理を実現するにあたっては,animate要素とmouseoverイベント及びmouseoutイベントとを組み合わせて対応した.単なる値の変更ならset要素が使えると思う.
  • 針の動きにはanimate要素を用いた.このままでは現在時刻を反映しないため,javascriptを使ってsvgロード時に現在時刻の分だけ初期位置をずらすようにした.ただしこの方法だと長期的には表示時刻と実際の時刻とがズレる可能性があるので,正確さを求めるのであればsetIntervalを使い時刻合わせをすると良い.
できたーと思ったら,operaで動いていないことに気がついた.orz…

<?xml version="1.0" standalone="no"?>
<svg onload="start();" width="150px" height="150px" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
 <style type="text/css">
  #hour,#minute,#second{
   stroke-linecap:round;
  }
  #hour{
   stroke:rgb(255, 128, 255);
   stroke-width:21;
  }
  #minute{
   stroke:rgb(255, 255, 128);
   stroke-width:14;
  }
  #second{
   stroke:rgb(128, 255, 255);
   stroke-width:7;
  }
  #big-c,#small-c{
   fill:white;
  }
 </style>
 <script>
function start(){
 //初期値を設定する
 //NOTE:針の動きをanimate要素に任せるのは時刻がズレる可能性があるため,
 //正確を期す場合は都度rotateの値を書き換えるようにする.
 var hour = document.getElementById("hour");
 var minute = document.getElementById("minute");
 var second = document.getElementById("second");
 var now = new Date();
 var h = now.getHours(), m = now.getMinutes(), s = now.getSeconds();
 var hs = h * 60 * 60 + m * 60 + s;
 var ms = m * 60 + s;
 var TRANSFORM = "transform";
 hour.setAttribute(TRANSFORM, getRotateStr(30 * hs / 3600));
 minute.setAttribute(TRANSFORM, getRotateStr(6 * ms / 60));
 second.setAttribute(TRANSFORM, getRotateStr(6 * s));
 function getRotateStr(deg){
  return "rotate(" + deg + ",100,100)";
 }
}
 </script>
 <defs>
  <circle id="big-c" cx="100" cy="10" r="10"/>
  <circle id="small-c" cx="100" cy="10" r="7"/>
 </defs>
 <image x="0" y="0" width="100%" height="100%" xlink:href="http://www.konami.jp/products/dl_xbox_dracula_hd/images/charlotte.jpg"/>
 <g>
  <g>
   <circle cx="100" cy="100" r="100" fill="blue" opacity="0.2"/>
   <use xlink:href="#big-c" transform="rotate(0,100,100)"/>
   <use xlink:href="#big-c" transform="rotate(90,100,100)"/>
   <use xlink:href="#big-c" transform="rotate(180,100,100)"/>
   <use xlink:href="#big-c" transform="rotate(270,100,100)"/>
   <use xlink:href="#small-c" transform="rotate(0,100,100)"/>
   <use xlink:href="#small-c" transform="rotate(30,100,100)"/>
   <use xlink:href="#small-c" transform="rotate(60,100,100)"/>
   <use xlink:href="#small-c" transform="rotate(120,100,100)"/>
   <use xlink:href="#small-c" transform="rotate(150,100,100)"/>
   <use xlink:href="#small-c" transform="rotate(210,100,100)"/>
   <use xlink:href="#small-c" transform="rotate(240,100,100)"/>
   <use xlink:href="#small-c" transform="rotate(300,100,100)"/>
   <use xlink:href="#small-c" transform="rotate(330,100,100)"/>
  </g>
  <line id="hour" x1="100" y1="30" x2="100" y2="40">
   <animateTransform attributeName="transform" type="rotate" 
    begin="0s" dur="12h" additive="sum" from="0,100,100" to="360,100,100" 
    repeatCount="indefinite" calcMode="linear"/>
  </line>
  <line id="minute" x1="100" y1="20" x2="100" y2="60">
   <animateTransform attributeName="transform" type="rotate" 
    begin="0s" dur="1h" additive="sum" from="0,100,100" to="360,100,100" 
    repeatCount="indefinite" calcMode="linear"/>
  </line>
  <line id="second" x1="100" y1="10" x2="100" y2="80">
   <animateTransform attributeName="transform" type="rotate" 
    begin="0s" dur="1min" additive="sum" from="0,100,100" to="360,100,100" 
    repeatCount="indefinite" calcMode="linear"/>
  </line>
  <circle id="clock_hover" cx="100" cy="100" r="100" fill="transparent"/>
  <animate attributeName="opacity" begin="clock_hover.mouseover" dur="0.5s" fill="freeze" to="0"/>
  <animate attributeName="opacity" begin="clock_hover.mouseout" dur="0.5s" fill="freeze" to="1"/>
 </g>
</svg>

0 件のコメント:

コメントを投稿