細かすぎて伝わらないd3 ver.4の話
TRANSCRIPT
プロフィール
清水正行 FB TW
群馬県高崎市在住
GUNMA GIS GEEK - Blog2015/11 - 転職毎日D3.jsを書いています
コンプライアンスにより社名は秘匿。(申請書めんどい)
Releasesv3
2012年12月22日 - 3.0.02016年5月5日 - 3.5.17
v4
2016年1月5日 - 4.0.0 α2016年6月24日 - 4.0.0 rc2016年6月28日 - 4.0.02016年10月27日 - 4.3.0
モジュール化
D3 v3は一つの巨大なライブラリでしたが、v4では機能ごとのモジュールに変更されました。モジュールはそれ単体でインストール
して利用することができます。
npm install d3‐array
<script src="https://d3js.org/d3‐array.v1.min.js"></script>
webpackやrollup.jsを使って、必要なモジュールだけを選んでカス
タムビルドすることもできるようになりました。
名前空間
モジュルー化に伴い名前空間の見直しが行われています。
スケール
d3.scale.linear ‐> d3.ScaleLineard3.scale.sqrt ‐> d3.scaleSqrt
レイアウト
d3.layout.cluster ‐> d3.clusterd3.layout.hierarchy ‐> d3.hierarchyd3.layout.pack ‐> d3.pack
スケール
d3.scale.ordinal().rangePoints() ‐> d3.scalePoint() d3.scale.ordinal().rangeBand() ‐> d3.scaleBand()
レイアウト
d3.layout.force() ‐> d3.forceSimulation()
Axis
d3.svg.axis().orient('left') ‐> d3.axisLeft()
細かい仕様の変更もある
オブジェクトリテラルを渡してのアップデートはできなくなった
v3 enable
d3.select("cicle") .attr({ "cx": 10, "cy": 20, "r":10 })
v4 only
d3.select("cicle") .attr("cx", 10) .attr("cy", 20) .attr("r", 10)
D3 v4を学ぶには?
チェンジログ読め。
CHANGES.md (長い)リファレンス読め。
D3 API Reference (膨大)下記スライドを読め。
D3 V4 - What's new? (103枚)
セレクター基本操作
//セレクトvar selected = d3.select("circle")
//データバインドselected.data([1, 10, 100, 100])
//足りない要素を追加var appented = selected.enter().append("circle")
//多すぎる要素を削除var deleted = selected.exit().remove()
//アップデートselected.attr("fill", "red")appentede.attr("fill", "red")
セレクタの戻り値が変わりました
v3
> var svg = d3.select("svg")> svg[Array[1]]
v4
> var svg = d3.select("svg")> svg{_groups: Array[1], _parents: Array[1]}
セレクターオブジェクト TIPS> var g = d3.select("svg").selectAll("g")> g.data([100, 200, 300]).enter().append("g")> g{ _groups: Array[1], _parents: Array[1]}
> g.data([1000, 2000, 3000, 4000])> g{ _groups: Array[1], _parents: Array[1], _enter: Array[1], _exit: Array[1]}
セレクションマージ
セレクタとセレクタをマージして、両方を含む新たなセレクタを
生成できます。
var selected = d3.select("circle") .data([1, 100, 1000])
var appented = selected.enter().append("circle")
//マージして一括アップデートselected.merge(appented) .attr("fill", "red")
セレクションフィルター
選択済みのセレクターをフィルタリングしてアップデートを適用
することができます。
var circles = d3.selectAll("circle")
circles.filter(function(d, i) { return i%2 }) .transition() .attr("cx", 0)
疑似クラスでの指定もできます。
circle.filter(":nth‐child(even)").attr("cx", 0)
非同期通信をまとめて処理
d3.queue() .defer(d3.tsv, 'data1.tsv') .defer(d3.json, 'japan.geojson') .await(function(error, tsv, geojson) { console.log(tsv) console.log(geojson) })
d3.queue() .defer(d3.tsv, 'data1.tsv') .defer(d3.json, 'japan.geojson') .awaitAll(function(error, results) { console.log(results) })
v3 - d3.nest> var data = [ {name:"a", value:10}, {name:"b", value:20}, {name:"a", value:30}]
> var nested = d3.nest() .key(function(d){ return d.name }) .map(data) > nested{a: Array[2], b: Array[1]}
v4 - d3.nest戻り値が、d3コレクションオブジェクトになりました。
> var data = [ {name:"a", value:10}, {name:"b", value:20}, {name:"a", value:30}]
> var nested = d3.nest() .key(function(d){ return d.name }) .map(data)
> nested{$a: Array[2], $b: Array[1]} // <‐ d3‐collection Object
> var map = d3.map({foo:1})
> map.set("bar", 2) .set("hoge", "aaaa") > map.get("hoge")aaaa
> map.keys()["foo", "bar", "hoge"]
> map.values()[1, 2, "aaaa"]
> map.entries()[ {"key":"foo","value":1}, {"key":"bar","value":2}, {"key":"hoge","value":"aaaa"}]
D3では階層構造のデータを必要とすることが多い
{ "name": "Eve", "children": [ { "name": "Cain" }, { "name": "Seth", "children": [ { "name": "Enos" }, { "name": "Noam" } ] }, ・・・}
フラットなオブジェクトを階層構造へ変換する
var data = [ {"name": "Eve", "parent": ""}, {"name": "Cain", "parent": "Eve"}, {"name": "Seth", "parent": "Eve"}, {"name": "Enos", "parent": "Seth"}]
var stratifyData = d3.stratify() .id(function(d){ return d.name }) .parentId(function(d){ return d.parent }) (data)
csv -> 構造化データ
id,value1,value2日本,日本.群馬県,日本.群馬県.高崎市,100,2000日本.群馬県.前橋市,200,1000日本.群馬県.桐生市,130,3000日本.埼玉県,日本.埼玉県.さいたま市,140,3200日本.埼玉県.所沢市,210,2400日本.東京都,日本.東京都.千代田区,日本.東京都.千代田区.大手町,220,1000
var stratify = d3.stratify() .parentId(function(d) { return d.id.substring(0, d.id.lastIndexOf(".")) }) var root = stratify(tsv) .sum(function(d) { return d.value1 })
イベントリスナー
トランジションの終了時にイベントが発行されるようになりまし
た。
var circle = d3.select("circle")
circle .transition() .duration(2000) .attr("cx", 500) .on("start", function(){ console.log("start!") }) .on("end", function(){ console.log("end!") })
トランジションチェイン
AからBへ移動して、さらにCへと移動するなど複雑なアニメーシ
ョンが可能に。
var circle = d3.select("circle")
circle .transition() .duration(1000) .attr("cx", 500) <‐‐x軸を移動 .transition() .duration(1000) .attr("cy", 500) <‐‐ y軸を移動
共通オブジェクト
メソッドチェインの数を減らし、複数のオブジェクトを同期して
アニメーションするなどが簡単にできるようになりました。
var t = d3.transition() .duration(2000) circle.transition(t) .attr("cx", 1000) rect.transition(t) .attr("x", 1000)
イベントも共通のリスナーを設定できる。
var t = d3.transition() .duration(2000) .on("end", function(){ alert("トランジション終了") }) circle.transition(t) .attr("cx", 1000) rect.transition(t) .attr("x", 1000)
Fit Extentd
v3 & v4
var projection = d3.geoMercator() .scale(2000) .center([139.5, 34])
v4.3.0
var projection = d3.geoMercator() .fitExtent([[0, 0], [width, height]], geojson)
幅と高さを指定してgeojsonを渡すと、いい感じに表示するための
プロジェクションを返す。
レスポンシブな地図を描画しやすくなった。
var line = d3.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.value); })
SVGレンダリング
path.datum(data).attr("d", line)
canvasレンダリング
var context = d3.select("canvas").node().getContext("2d")line.context(context)(data)
地図もcanvasにレンタリグングできます
var context = d3.select("canvas").node().getContext("2d")
var projection = d3.geoMercator() .fitExtent([[0, 0], [chartWidth, chartHeight]], geojson)
var geoPath = d3.geoPath().projection(projection) .context(context)
geoPath(geojson) <‐ canvas Draw Maps
v3 Force Layout
おなじみのコード。
var force = d3.layout.force() .nodes(nodes) .links(links) .size([width, height]) .gravity(0.1) .charge(‐30) .friction(0.95) .linkDistance(220) .linkStrength(1)
v4 Force Simulation
全然違う。
var simulation = d3.forceSimulation() .force("link", d3.forceLink() .id(function(d){ return d.index }) .distance(220)) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter(width / 2, height / 2)) .force("y", d3.forceY(0)) .force("x", d3.forceX(0))
サイズ指定ではなくセンター指定になった
V3
var force = d3.layout.force() .size([width, height])
V4
var simulation = d3.forceSimulation() .force("center", d3.forceCenter(width / 2, height / 2))
ノードの衝突判定
新たにノード同士の衝突判定を行う機能が追加
var simulation = d3.forceSimulation() .force("collide",d3.forceCollide(function(d){ return d.r + 8 //半径に対して8pxのマージンを指定 }).iterations(16) )
座標に最も近いノードを見つける
var simulation = d3.forceSimulation()simulation.find(x, y, r)
マウスイベントと連動させる
svg.on("mousemove", function(){ var findNode = simulation.find(d3.event.x, d3.event.y) console.log(findNode)})