前の投稿で、D3.jsのtransitionを利用したsvgオブジェクトの変化を扱いました。
Flashに頼らずとも、簡単にオブジェクトを移動できる点がすばらしいと思います。
今回は、その続きです。
transitionを利用して、オブジェクトが設定範囲内を文字通り「駆け巡る」ようにします。
ウェブ検索で見つけた方法
transitionを解説したウェブページが幾つかあります。そこで紹介されているのは次のようなやり方です。
まず、前回同様、SVG上に円を一つ描きます。
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"> var svg = d3.select("body").select("div") .append("svg") .attr("width",600) .attr("height",450) .style("background-color", "black"); svg .append("circle") .attr("r",30) .attr("cx",100) .attr("cy",100) .style("fill","blue"); </script>
これを動かすのですが、transitionを繰り返し記述するだけです。ポイントはDelayの設定です。
前回のtransitionが終わってからの時間ではなく、全体を動かし始めてからの通算時間で設定しましょう。
この例では、円の中心座標(100,100)が1秒ごとに,(100,300),(300,300),(100,100)と移動し、その後、円の半径が1pxまで縮小します。
svg .select("circle") .transition() .delay(0) .duration(1000) .attr("cy", 300); svg .select("circle") .transition() .delay(1000) .duration(1000) .attr("cx", 300); svg .select("circle") .transition() .delay(2000) .duration(1000) .attr("cx", 100) .attr("cy", 100); svg .select("circle") .transition() .delay(3000) .duration(1000) .attr("r", 1);
このやり方は、移動回数が少ない時にはいいのですが、円をどんどん動かしたいときには向いていません。
移動のたびにtransitionを書いているからです。
そこで別のやり方を紹介します。
data機能を利用した連続的変化
SVG上に円を一つ描くところまでは同じです。
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"> var svg = d3.select("body").select("div") .append("svg") .attr("width",600) .attr("height",450) .style("background-color", "black"); svg .append("circle") .attr("r",30) .attr("cx",100) .attr("cy",100) .style("fill","blue"); </script>
次に、乱数を生成するコードMath.random()を利用して、円の中心座標を50個ほど決めて、arrという配列に格納します。
var arr = []; for (var i = 0; i < 50; i++) { arr.push(new Object({x: (Math.random() * 600), y: (Math.random() * 450)})); }
このランダムに決めた50個の座標にしたがって、先ほどの円がSVG上をかけるようなアニメーションを作ります。
データ配列をtransitionに利用する場合、data()とenter()を使った方がよさそうに思えます。
でも、そうすると50個の円が生成されてしまい、一つの円の座標のコントロールになりません。
そこでforEach()を使います。forEach()はD3.js独自のコードでforループに相当する操作を行います。
今回はsvgよりも一つ上位のスコープで配列の各要素を操作をします。
これによって一つの円だけを操作できます。
また、delayの使い方も重要です。この例ではi*300とすることで、データの要素順に移動のタイミングが300msずつずれています。
arr.forEach(function(d,i){ svg .select("circle") .transition() .delay(i * 300) .duration(300) .attr("cx", d.x) .attr("cy", d.y); });
ここまでをまとめると以下のようになります。