概要
Force Layoutを使いたいがためだけに使い方を覚える。
D3とはData Driven Docuement。
データを視覚化するライブラリ。
サンプル
Hello D3.js
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Test</title>
</head>
<body>
<p>Hello 1</p>
<p>Hello 2</p>
<p>Hello 3</p>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
d3.select('body').selectAll('p').text('hello from d3!');
</script>
</body>
</html>
要素の取得、テキスト/スタイルの変更
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Test</title>
</head>
<body>
<p>Hello 1</p>
<p>Hello 2</p>
<p>Hello 3</p>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var p = d3.select('body').selectAll('p')
p.text('hello from d3!');
p.style('font-size', '28px');
p.text('hello from d3!')
.style('font-size', '28px')
.style('font-weight', 'bold');
p.text('hello from d3!')
.style({
'font-size': '28px',
'font-weight': 'bold'
});
// alert(p.style('font-size'));
p.style('font-size', function() {
return Math.floor(Math.random() * 32) + 'px';
});
</script>
</body>
</html>
要素の削除
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Test</title>
</head>
<body>
<p>Hello 1</p>
<p>Hello 2</p>
<p>Hello 3</p>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var obj = d3.select('body').append('p').text('hello 4');
obj.remove();
</script>
</body>
</html>
dataを使ってみる
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Test</title>
</head>
<body>
<p>Hello 1</p>
<p>Hello 2</p>
<p>Hello 3</p>
<span></span>
<span></span>
<span></span>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var dataset = [12, 24, 36];
var p = d3.select('body').selectAll('p');
p.data(dataset).text(function(d) {
return d;
});
p.data(dataset).text(function(d, index) {
return index + "番目は" + d;
});
var dataset2 = [{name: 'aki', age: 1},{name: 'mii', age: 2},{name: 'akiton', age: 3}];
var span = d3.select('body').selectAll('span');
span.data(dataset2).text(function(d, index) {
return d.name + '(' + d.age + ')';
});
</script>
</body>
</html>
update, enter, exit
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Test</title>
</head>
<body>
<p>Hello 1</p>
<p>Hello 2</p>
<p>Hello 3</p>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var dataset = [12, 24, 36, 48];
var p = d3.select('body').selectAll('p');
var update = p.data(dataset);
var enter = update.enter();
var exit = update.exit();
update.text(function(d, index) {
return "update: " + d;
});
enter.append('p').text(function(d, index) {
return "enter: " + d;
});
exit.style('color', 'red').remove();
</script>
</body>
</html>
データから図形を描画する
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Test</title>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var dataset = [12, 24, 36, 48, 33];
var w = 640;
var h = 480;
var svg = d3.select('body').append('svg').attr({
width: w,
height: h
});
svg.selectAll('circle')
.data(dataset)
.enter()
.append('circle')
.attr({
cx: function(d, i) { return 50 + (i * 100); },
cy: h / 2,
r: function(d) { return d; },
fill: 'red'
});
</script>
</body>
</html>
図形描画時にアニメーションさせてみる
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Test</title>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var dataset = [12, 24, 36, 48, 33];
var w = 640;
var h = 480;
var svg = d3.select('body').append('svg').attr({
width: w,
height: h
});
svg.selectAll('circle')
.data(dataset)
.enter()
.append('circle')
.attr({
cx: function(d, i) { return 50 + (i * 100); },
cy: h / 2,
r: 0,
fill: 'red'
})
.transition()
.delay(function(d, i) {
return 300 * i;
})
.duration(1000)
.ease('bounce')
.attr({
r: function(d) { return d; }
});
</script>
</body>
</html>
eachを使ってアニメーションを制御してみる
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Test</title>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var dataset = [12, 24, 36, 48, 33];
var w = 640;
var h = 480;
var svg = d3.select('body').append('svg').attr({
width: w,
height: h
});
svg.selectAll('circle')
.data(dataset)
.enter()
.append('circle')
.transition()
.delay(function(d, i) {
return 300 * i;
})
.duration(1000)
.ease('bounce')
.each('start', function() {
d3.select(this).attr({
fill: 'green',
r: 0,
cy: h
});
})
.attr({
cx: function(d, i) { return 50 + (i * 100); },
cy: h / 2,
r: function(d) { return d; },
fill: 'red'
})
.each('end', function() {
d3.select(this)
.transition()
.duration(800)
.attr({
fill: 'pink',
cy: 0,
r: 0
});
});
</script>
</body>
</html>
イベントを取得する
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Test</title>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var dataset = [12, 24, 36, 48, 33];
var w = 640;
var h = 480;
var svg = d3.select('body').append('svg').attr({
width: w,
height: h
});
svg.selectAll('circle')
.data(dataset)
.enter()
.append('circle')
.on('mouseover', function(d) {
d3.select(this).attr('fill', 'orange');
})
.on('mouseout', function(d) {
d3.select(this).attr('fill', 'red');
})
.on('mousedown', function(d) {
var rs = d3.select(this).attr('r');
alert(d + ':' + rs);
})
.attr({
cx: function(d, i) { return 50 + (i * 100); },
cy: h / 2,
r: function(d) { return d; },
fill: 'red'
})
</script>
</body>
</html>
棒グラフを書く
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Test</title>
<style>
.axis path, .axis line {
fill: none;
stroke: black;
}
.axis text {
font-size: 11px;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var dataset = [12, 24, 36, 48, 33];
var w = 640;
var h = 480;
var padding = 20;
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([padding, w - padding])
.nice();
var svg = d3.select('body').append('svg').attr({
width: w,
height: h
});
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom');
svg.append('g')
.attr({
class: 'axis',
transform: 'translate(0, 180)'
})
.call(xAxis);
svg.selectAll('rect')
.data(dataset)
.enter()
.append('rect')
.attr({
x: padding,
y: function(d, i) { return i * 25; },
width: function(d) { return xScale(d) - padding; },
height: 20,
fill: 'red'
});
</script>
</body>
</html>
後からノード追加して動かしたり
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3.js Force Layout</title>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var width = 640;
var height = 480;
var svg = d3.select('body').append('svg').attr({
width: width,
height: height
});
var data = {
nodes: [
{ name: 'aki' },
{ name: 'mii' },
{ name: 'akiton' }
],
links: [
{ source: 0, target: 2 },
{ source: 1, target: 2 }
]
};
var force = d3.layout.force()
.nodes(data.nodes)
.links(data.links)
.size([width, height])
.gravity(0.1)
.charge([-100])
.friction(0.95)
.linkDistance([100])
.linkStrength(1);
// レイアウト計算開始
force.start();
for (var i = 0; i < 10000; i++) {
force.tick();
}
force.stop();
// lineを生成
var line = svg.selectAll('line')
.data(data.links)
.enter()
.append('line')
.attr({
'stroke': 'black',
'x1': function(d, i) { return d.source.x; },
'y1': function(d, i) { return d.source.y; },
'x2': function(d, i) { return d.target.x; },
'y2': function(d, i) { return d.target.y; }
})
.style({
});
var circle = svg.selectAll('circle')
.data(data.nodes)
.enter()
.append('circle')
.attr({
'r': 8,
'fill': 'black',
'cx': function(d, i) { return d.x; },
'cy': function(d, i) { return d.y; }
})
.style({
});
// .call(force.drag);
var moveTarget = null;
circle
.on('mousedown', function(d, i) {
console.log(d.name + '(' + d.x + ',' + d.y + ')');
moveTarget = d;
})
.on('mouseup', function(d, i) {
moveTarget = null;
});
svg.on('mousemove', function(e) {
if (moveTarget != null) {
var mousePos = d3.mouse(this);
console.log('moveTarget = ' + moveTarget.name + ' (' + mousePos[0] + ',' + mousePos[1] + ')');
moveTarget.x = mousePos[0];
moveTarget.y = mousePos[1];
d3.selectAll('line')
.attr({
'x1': function(d) { return d.source.x; },
'y1': function(d) { return d.source.y; },
'x2': function(d) { return d.target.x; },
'y2': function(d) { return d.target.y; }
});
d3.selectAll('circle')
.attr({
'cx': function(d) { return d.x; },
'cy': function(d) { return d.y; }
});
}
});
svg.on('mousedown', function() {
if (moveTarget != null) return;
var newCircle = svg.append('circle');
var mousePos = d3.mouse(this);
var newData = {
name: 'super',
age: 10,
x: mousePos[0],
y: mousePos[1]
};
data.nodes.push(newData);
console.log('add circle: ' + newData.x + ',' + newData.y);
newCircle
.datum(newData)
.attr({
'cx': function(d, i) { console.log(d);return d.x; },
'cy': function(d, i) { return d.y; },
'r': 10,
'fill': 'red'
})
.on('mousedown', function(d) {
moveTarget = d;
})
.on('mouseup', function(d) {
moveTarget = null;
});
});
function randomMove() {
for (var i = 0; i < data.nodes.length; i++) {
var d = data.nodes[i];
d.x += Math.random() * 2 <= 1 ? Math.random()*10 : -Math.random()*10;
d.y += Math.random() * 2 <= 1 ? Math.random()*10 : -Math.random()*10;
d3.selectAll('line')
.attr({
'x1': function(d) { return d.source.x; },
'y1': function(d) { return d.source.y; },
'x2': function(d) { return d.target.x; },
'y2': function(d) { return d.target.y; }
});
d3.selectAll('circle')
.attr({
'cx': function(d) { return d.x; },
'cy': function(d) { return d.y; }
});
}
}
</script>
<input type="button" value="randomMove" onClick="randomMove()"/>
</body>
</html>