Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

SVG with Javascript習作

SVG女子が話題を呼んでいることもあり、連休中にSVGに触れました。作ったのはJavascriptで動的に図形を描画したり動かしたりするもの。ジャンルとしてはSRPGタワーディフェンスゲームに近いと言えます。

forked from: SRPG Map (Hex, SVG) - jsdo.it - share JavaScript, HTML5 and CSS


その他にもいくつか書きましたので、興味をお持ちの方はご覧いただけると嬉しいです。

以下、気づいたことをメモしておきます。なお開発環境はMac OSX 10.6.7 + Chrome 11.0.696.65でした。

SVGjQueryとの相性が悪い

今回はjQuery 1.5.2を利用しましたが、attr()やaddClass()がrectなどのDOM要素に使えませんでした。具体的には、attr()などで書き換えた属性やクラスが描画に反映されませんでした。Chromeの開発者ツールで見る限り、属性の更新には成功しているようなのですが……。
stackoverflowなどを見る限りではこの問題は既知らしく、一般にはjQuery SVGなどのjQueryプラグインを利用して回避するようです。が、jsdo.itで手軽に書きたかったこととSVGを直に触っておきたかったことから、今回はjQueryを使わずにDOM要素を生成・操作する方法をとりました。例えば矩形(マップチップ)を生成して配置するスクリプトは以下のようになります。

var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
rect.setAttribute('x', 32);
rect.setAttribute('y', 64);
rect.setAttribute('width', CHIP_SIZE);
rect.setAttribute('height', CHIP_SIZE);
rect.setAttribute('class', 'chip chipKind1');
rect.id = 'x1y2';
$svg.append(rect);

見栄えに関する情報はCSSに切り出せる

SVGcanvasと比較した場合、最も大きな利点はこれではないかと思います。Javascriptのコードからデザインに関する記述を除き、ロジックに集中させることが可能です。
SVGの特徴としてよく言及される「拡大してもボケない」も確かに利点なのですが、そもそも拡大する機会が少ないのであまり嬉しくありません。iPadのようなタブレットで動くようになると、わりと嬉しいんでしょうか。

今回はマップチップの着色をCSSで行いました。stroke-widthプロパティで線の太さを、strokeプロパティで線の色を、fillプロパティで塗りつぶす色を指定しています。

.chip {
    stroke-width: 2;
    stroke: #000;
}
.chipKind0 {
    fill: #0f0;
}

z-indexは使えない

SVGの要素同士が重なりあった場合、後から追加された要素が手前側に表示されます。cssのz-indexが使えればよかったのですが、少なくとも今のところはそのような挙動になっていません。試した限りでは、兄弟要素同士でもダメでした。

@uupaa SVG では z-index も一応あるけど、同一親要素に対する兄弟同士でのみ有効=親子関係が優先ですね。http://bit.ly/c9R3Ba DOM いじるしかないと SVG Wiki に書いてあります http://bit.ly/ddqYOMless than a minute ago via Echofon Favorite Retweet Reply

今回は「手前に表示したい」ケースだけで「奥に表示したい」というケースはなかったので、持ってきたい要素をSVG要素から削除して追加しなおすメソッドを用意しました。

function moveToTop(id) {
    var e = document.getElementById(id);
    svg.removeChild(e);
    svg.appendChild(e);
}

座標系が面倒

Javascript標準のMath.cos()などは角度を弧度法で扱いますが、SVGは角度を度数法で扱います。このため図形を回転させる場合などは“ラジアン→度”の変換が必要です。

var deg = rad * 180 / Math.PI;

また回転させた図形をsetAttributeで移動させる際にも注意が必要です。図形を回転させた際に図形の座標系も回転している事に気づかず、時間をロスしました。

// from http://jsdo.it/eller86/qcC9
$.each(weapons, function(i, w){
    var elem = document.getElementById(w.id);
    if (!elem) { return; }
    elem.setAttribute('x', w.r);  // X座標しか変更していないが、回転時の角度に沿って斜めに移動してくれる
};

iPhone(Safari)で動かない?

残念ながら今回の習作はSafariFirefoxで動作しません。SVGcanvasと比較したとき、この動作環境の制限の強さが最大の欠点になるでしょう。

……と思っていたのですが、こちらのサンプルiPodSafariできちんと表示できるようです。今回の習作ではDOCTYPE宣言をきちんと行なっていなかったりhtml要素の属性で名前空間を指定していなかったりするので、このあたりで問題が生じているのかもしれません。