D3.jsのscale, transition, data binding覚え書き

前回のエントリー!では、とりあえず表示できるところまでやりました。今回はもう少しD3らしい形にしていきます。

d3.scale, selection.data()

var minHeight = d3.min(member, function(d){return d.height});
var maxHeight = d3.max(member, function(d){return d.height});
var hScale = d3.scale.linear()
    .domain([minHeight, maxHeight])
    .range([h - offset, offset]);

入力領域はdomain、出力範囲はrangeで指定する。

ここではminHeightからmaxHeightの入力領域を、表示領域(svgのサイズ)の端からoffsetを引いたところからoffset分ずらしたもう一端まで等間隔に対応させる数値にします。

つまりhScale(にこ先輩の身長) = offset, hScale(エリーチカの身長) = h -offset

var graph = svg.selectAll(".select")
    .data(member);

var group = graph.enter().append("g")
    .attr("class", "select");

var circles = group.append("circle")
    .attr("class", "select")
    .attr("cx", function(d, i) { return wScale(i); })
    .attr("cy", r + offset)
    .attr("r", r)
    .attr("fill", function(d) { return "rgb(" + d.color + ")"; });

selection.data()でbindingしたデータが、最終行のfunctionに引数として渡されています。なお、第二引数は呼びだし順の数値です。

g, transform

svgのg要素は、htmlのdivのような感じです。transform属性を指定することによって、含まれる要素をまとめて動かしたり変形することが出来ます。

例えば<g transform="translate(10, 20)">とすると、右に10, 下に20移動します。

group.transition()
    .attr("transform", function(d, i) {
        return "translate(0," + (hScale(d.height) - r - offset) + ")";
    });

transition

transformのソースに、selection.transition().attr()という形があります。

attr()で行われる変更にトランジション効果を設定しています。

selection.transition().
      .delay(750).
      .duration(750)
    .attr("r", 30);

この場合は、delay, durationともに750msでrを30に変更します。

ソースとサンプル

今回の動作サンプル

ソース

<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="UTF-8">
   <style type="text/css">
        circle {
            stroke: #333;
            stroke-width: 1.5px;
        }
        text {
            font: 10px sans-serif;
        }
        text.shadow {
            stroke: #fff;
            stroke-width: 3px;
            stroke-opacity: .8;
        }
    </style>
</head>
<body>

<div id="graph">
<button>Run</button>
</div>

<script type="text/javascript" src="d3.v3.min.js"></script>

<script>
d3.json("https://dl.dropbox.com/u/450748/d3.v3/mus.json", function(error, json) {
var member = json.nodes;
member.sort(function(a, b) { return b.height - a.height; });

var w = 720;
var h = 540;

var r = 30;
var offset =  r * 2;

var svg = d3.select("#graph").append("svg")
    .attr("width", w)
    .attr("height", h);

var graph = svg.selectAll(".select")
    .data(member);

var wScale = d3.scale.linear()
    .domain([0, member.length - 1])
    .range([offset, w - offset]);

var minHeight = d3.min(member, function(d){return d.height});
var maxHeight = d3.max(member, function(d){return d.height});
var hScale = d3.scale.linear()
    .domain([minHeight, maxHeight])
    .range([h - offset, offset]);

var group = graph.enter().append("g")
    .attr("class", "select");

var circles = group.append("circle")
    .attr("class", "select")
    .attr("cx", function(d, i) { return wScale(i); })
    .attr("cy", r + offset)
    .attr("r", r)
    .attr("fill", function(d) { return "rgb(" + d.color + ")"; });

var text = group.append("text")
    .attr("class", "select shadow")
    .attr("x", function(d, i) { return wScale(i); })
    .attr("y", r + offset)
    .attr("text-anchor", "middle")
    .text(function(d) { return d.name; });

var textShadowed = group.append("text")
    .attr("class", "select")
    .attr("x", function(d, i) { return wScale(i); })
    .attr("y", r + offset)
    .attr("text-anchor", "middle")
    .text(function(d) { return d.name; });

group.transition()
    .attr("transform", function(d, i) {
        return "translate(0," + (hScale(d.height) - r - offset) + ")";
    });


var yAxis = d3.svg.axis()
    .scale(hScale)
    .orient("left");

svg.append("g")
    .attr("transform", "translate(" + r + ",0)")
    .attr("class", "select")
    .call(yAxis);


// button click
d3.select("#graph button").on("click", function() {
    var delayTime = 750;
    var durationTime = 750;

    // r
    circles.transition()
        .delay(delayTime)
        .duration(durationTime)
        .attr("r", function(d) { return d.B/2; });

    var minB = d3.min(member, function(d){return d.B});
    var maxB = d3.max(member, function(d){return d.B});
    var wScaleByB = d3.scale.linear()
        .domain([minB, maxB])
        .range([offset, w - offset]);

    group.transition()
        .delay(delayTime)
        .duration(durationTime)
        .attr("transform", function(d, i) {
            return "translate(" + (wScaleByB(d.B) - wScale(i)) + "," + (hScale(d.height) - r - offset) +")";
        });


    var xAxis = d3.svg.axis()
        .scale(wScaleByB)
        .orient("bottom");

    var g = svg.append("g")
        .attr("class", "select")
        .attr("opacity", 0)
        .call(xAxis);
    g.transition()
        .duration(durationTime)
        .delay(delayTime)
        .attr("opacity", 1);

});

});
</script>

</body>
</html>