huruyosi’s blog

プログラミングとかインフラとかのメモです。

d3.js の General Update Pattern, II を読む

データにキーを持たせる方法

d3.js の General Update Pattern, I を読む - huruyosi’s blog でのデータ更新は配列の要素に基づいた方法であったので、サンプルを見ていても「更新」されたとするデータがみためとはずれている感覚がありました。

General Update Pattern, II ではデータにキーを持たせることで見た目とのズレがなくなりました。

f:id:huruyosi:20160131113806p:plain

二つのソースを比べるとデータの与え方を変え、それによって.enter()で行う要素の追加と文字の描画方法も変えています。

データの与え方

 var text = svg.selectAll("text")
      .data(data, function(d) { return d; });

dataメソッドの第2引数にデータに対応する一意なキーを返す無名関数を指定しています。第2引数の無名関数の引数にはデータが与えられるので、このサンプルではアルファベット1文字が渡ってきます。引数をそのまま返すことで、アルファベットがキーになり、値も同じアルファベットになります。

https://github.com/mbostock/d3/wiki/Selections#data

2回目以降の dataメソッドの呼び出しでは、同一のキーであればデータが更新され、新たなキーであれば追加なので.enter()の対象になり、無くなったキーであれば削除なので.exit()の対象になります。

データが追加された

 text.enter().append("text")
      .attr("class", "enter")
      .attr("dy", ".35em")
      .text(function(d) { return d; });

前回と違うのは .attr("x", function(d, i) { return i * 32; })がなくなり、.text(function(d) { return d; })が追加されています。キーと値が同じなので、.textレンダリングする文字は変化しないので追加時に設定します。一方、文字を描画するx座標は毎回変化することが考えられるので、はずされています。

追加と更新されたデータのX座標

text.attr("x", function(d, i) { return i * 32; })

データのn番目に基づいて文字を描画するx座標を決めています。

無くなったデータ

text.exit().remove();

d3.js の General Update Pattern, I を読む - huruyosi’s blog と同様に削除しています。

処理

// 表示するアルファベットの配列を作成します。
var alphabet = "abcdefghijklmnopqrstuvwxyz".split("");

var width = 960,
    height = 500;

// svgタグを作成し、gタグを縦方向の中央に移動します。
var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(32," + (height / 2) + ")");

function update(data) {

  // DATA JOIN
  // Join new data with old elements, if any.
  // データとデータのキーを取得する無名関数を与えます。
  var text = svg.selectAll("text")
      .data(data, function(d) { return d; });

  // UPDATE
  // Update old elements as needed.
 // 更新されたデータ
  // 前回の処理でも存在した要素のclassにupdateを指定することで文字色を指定します。
  text.attr("class", "update");

  // ENTER
  // Create new elements as needed.
  // 追加されたデータが対象
  // 前回の処理よりも増えた要素は新たななtextタグを作成し文字色を指定します。
  // また"text"属性で描画する文字を、"dy"属性でサイズを指定します。
  text.enter().append("text")
      .attr("class", "enter")
      .attr("dy", ".35em")
      .text(function(d) { return d; });

  // ENTER + UPDATE
  // Appending to the enter selection expands the update selection to include
  // entering elements; so, operations on the update selection after appending to
  // the enter selection will apply to both entering and updating nodes.
 // 追加と更新されたデータが対象
 // データのn番目に基づいて描画する文字のx座標を"x"属性で指定します。
  text.attr("x", function(d, i) { return i * 32; })

  // EXIT
  // Remove old elements as needed.
  // 削除されたデータが対象
  // 前回の処理では存在し、今回の処理では存在しない要素を削除します。
  text.exit().remove();
}

// The initial display.
// 最初の表示を行います。a~zまでの文字が描画されます。
update(alphabet);

// Grab a random sample of letters from the alphabet, in alphabetical order.
// 1.5秒ごとにアルファベットをランダムに決め、アルファベット順に表示します。
setInterval(function() {
  update(d3.shuffle(alphabet)
      .slice(0, Math.floor(Math.random() * 26))
      .sort());
}, 1500);