d3是基於HTML和SVG的數據可視化JS庫. 它將數據(data)和元素(DOM)相互綁定在一塊兒, 而且在數據實時改變狀況下DOM元素也會實時改變.javascript
因此, D3是Data-Driven Documents, 即數據驅動元素, 而D3的命名則來源於W3C Document Object Model.html
html模板文件爲:html5
<!DOCTYPE html> <meta charset="utf-8"> <style> </style> <script src="d3.js"></script> <script> </script>
咱們須要在當前html模板文件下啓動一個簡單的服務器, 這樣才能讀取數據文件:java
leicj@lishunan:~$ python -m SimpleHTTPServer 8888 & [1] 16960 leicj@lishunan:~$ Serving HTTP on 0.0.0.0 port 8888 ... 127.0.0.1 - - [23/Oct/2016 13:02:13] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [23/Oct/2016 13:02:13] code 404, message File not found 127.0.0.1 - - [23/Oct/2016 13:02:13] "GET /favicon.ico HTTP/1.1" 404 - 127.0.0.1 - - [23/Oct/2016 13:02:37] "GET /test/ HTTP/1.1" 200 - 127.0.0.1 - - [23/Oct/2016 13:02:37] "GET /test/d3.js HTTP/1.1" 200 -
d3的選擇器和jQuery很像, 如下是常常要用到的選擇器:python
#foo // <any id="foo"> foo // <foo> .foo // <any class="foo"> [foo=bar] // <any foo="bar"> foo bar // <foo><bar></foo> foo.bar // <foo class="bar"> foo#bar // <foo id="bar">
d3使用selectAll來篩選, 因此:git
d3.selectAll("pre,code")
等價於jQuery中:github
$("pre,code")
選擇器自己返回的是一個數組. 數組
一個實際的例子:服務器
<!DOCTYPE html> <meta charset="utf-8"> <style> svg { margin-top: 20px; margin-left: 20px; } </style> <svg height="960" width="960"> <circle></circle> </svg> <script src="d3.js"></script> <script> var circle = d3.selectAll("circle"); circle.attr('cx', 50); circle.attr('cy', 52); circle.attr('r', 24); circle.style("fill", "red"); </script>
繪製一個circle:app
而d3支持鏈式編寫:
d3.selectAll("circle") .attr('cx', 50) .attr('cy', 52) .attr('r', 24) .style("fill", "red");
selection.append用於建立一個新的元素, 選擇其元素, 並在其元素後append數據.
d3.select("body").append("h1") .text("Hello!");
或者建立多個小圓圈:
<svg height="960" width="960"> </svg> <script src="d3.js"></script> <script> var data = [5, 10, 15, 20, 25, 20, 15, 10, 5]; var g = d3.select('svg').append('g'), circle = g.selectAll('circle').data(data); circle.enter() .append("circle") .attr('cx', function(d, i) { return (i + 1) * 32; }) .attr('cy', function(d, i) { return d * 20; }) .attr('r', function(d, i) { return 10; }) .attr('fill', 'red'); </script>
咱們考慮下以下的圖形如何繪製:
這裏由五部分組成: 五個rect圖形, x軸的索引值, y軸的索引值, x軸, y軸. 開些編寫前, 先普及一些基本的svg函數:
1. rect: 用來繪製矩形, 基本屬性以下:
x: 矩形左上角的x位置
y: 矩形左下角的y位置
width: 矩形的寬度
height: 矩形的高度
rx: 圓角的x方位的半徑
ry: 圓角的y方位的半徑
2. translate: 使元素進行移動
首先, 咱們繪製五個rect:
var rect_arr = [ {"height": 40, "width": 100}, {"height": 40, "width": 200}, {"height": 40, "width": 300}, {"height": 40, "width": 400}, {"height": 40, "width": 500} ]; var g = d3.select("svg").append("g").attr("transform", "translate(40,60)"), rect = g.selectAll("rect").data(rect_arr); rect.enter() .append("rect") .attr("class", "bar") .attr("height", function(d, i) { return d.height; }) .attr("width", function(d, i) { return d.width; }) .attr("y", function(d, i) { return 80 + i * 70; }) .attr("fill", "steelblue");
效果圖以下:
其次, 咱們繪製x軸:
var x_arr = [0, 100, 200, 300, 400, 500, 600], gx = d3.select("svg").append("g").attr("class", "x axis").attr("transform", "translate(40, 460)"), xaxis = gx.selectAll("g").data(x_arr); xaxis.enter() .append("g") .attr("transform", function(d, i) { return "translate(" + d + ",0)"}) .attr("style", "opacity:1") .append("text") .attr("y", 14) .attr("x", 0) .attr("dy", ".71em") .attr("text-anchor", "middle") .text(function(d, i) { return i; });
和x軸的線:
var xaxis_path = d3.select("svg g.x").append("path") .attr("stroke", "#000") .attr("d", "M0 6H600");
y軸:
var y_arr = [ {"y": 20, "text": "A"}, {"y": 90, "text": "B"}, {"y": 160, "text": "C"}, {"y": 230, "text": "D"}, {"y": 300, "text": "E"} ], gy = d3.select("svg").append("g").attr("class", "y axis").attr("transform", "translate(40, 100)"), yaxis = gy.selectAll("g").data(y_arr); yaxis.enter() .append("g") .attr("transform", function(d, i) { return "translate(0," + (40 + d.y) + ")"}) .attr("style", "opacity:1") .append("text") .attr("x", "-8") .attr("y", 0) .attr("dy", ".32em") .attr("text-anchor", "end") .text(function(d, i) { return d.text; });
和y軸的線:
var yaxis_path = d3.select("svg g.y").append("path") .attr("stroke", "#000") .attr("d", "M0 0V370");
咱們來看如下做者給出的一個實例, 涉及到d3.js中的不少函數. 若是是初學d3.js, 須要查看文檔才能看懂如下代碼.
效果圖:
具體代碼:
<!DOCTYPE html> <html> <meta charset="utf-8"> <style> circle.dot { fill: steelblue; } .axis text { font: 10px sans-serif; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } </style> <body> <!--<script src="d3.js"></script>--> <script src="http://d3js.org/d3.v2.min.js" charset="utf-8"></script> <script> var data = [ {x: 10.0, y: 9.14}, {x: 8.0, y: 8.14}, {x: 13.0, y: 8.74}, {x: 9.0, y: 8.77}, {x: 11.0, y: 9.26}, {x: 14.0, y: 8.10}, {x: 6.0, y: 6.13}, {x: 4.0, y: 3.10}, {x: 12.0, y: 9.13}, {x: 7.0, y: 7.26}, {x: 5.0, y: 4.74} ]; var margin = {top: 40, right: 40, bottom: 40, left: 40}, width = 960, height = 500; var x = pad(d3.scale.linear() .domain(d3.extent(data, function(d) { return d.x; })) .range([0, width - margin.left - margin.right]), 40); var y = pad(d3.scale.linear() .domain(d3.extent(data, function(d) { return d.y; })) .range([height - margin.top - margin.bottom, 0]), 40); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .tickPadding(8); var yAxis = d3.svg.axis() .scale(y) .orient("left") .tickPadding(8); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height) .attr("class", "dot chart") .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); svg.selectAll(".dot") .data(data) .enter().append("circle") .attr("class", "dot") .attr("cx", function(d) { return x(d.x); }) .attr("cy", function(d) { return y(d.y); }) .attr("r", 12); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + y.range()[0] + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .call(yAxis); function pad(scale, k) { var range = scale.range(); if (range[0] > range[1]) k *= -1; return scale.domain([range[0] - k, range[1] + k].map(scale.invert)).nice(); } </script> </body> </html>
經過查詢API文檔, 解釋一些基本的函數:
domain和range相伴相生, domain用來設置比例尺, 而range用來設置實際的長度. 例如在一個SVG內部(寬度爲960), 咱們繪製X軸, 則range的範圍能夠是[0,900], 而domain表明實際的數據(例如5個點, x軸分別爲0, 1, 2, 3, 4), 則domain的範圍就是[0,4]. 因此第一個點繪製在0處, 中間2繪製在450處, 最後一個點4繪製在900處.
d3.extent: 用來找出數組中的最大值和最小值.
axis().scale: 用來設置比例尺
備註: 此代碼沒法理解, 須要學習到後面, 多聯繫, 才能夠本身完整寫出來.
以上兩個例子有難度. 如今來看看具體如何操做數據:
svg.selectAll("circle") .data(data) .enter() .append("circle") .attr("cx", x) .attr("cy", y) .attr("r", 2.5);
這裏咱們可能有疑惑, 爲何circle還爲存在狀況下, 咱們卻selectAll("circle")呢?
這裏是語法糖, 主要代碼塊應該這樣看: selectAll("circle").data(data); 它的做用是將data和DOM元素關聯起來, 使用enter()表明進入要遞歸的每一個元素, 而後append()生成此元素. 因此代碼實際上應該分開成:
var circle = svg.selectAll("circle") .data(data); circle.enter() .append("circle") .attr("cx", x) .attr("cy", y) .attr("r", 2.5);
Data-->Attributes: 屬性(attribute/style)是用來控制元素的位置和展示.
Domain-->Range: 用來控制data-space和visual-space.
var x = d3.scale.linear() .domain([12, 24]) .range([0, 720]); console.log(x(16)); // 240 = (16 - 12) * ((720 - 0) / (24 - 12)) + 0
咱們可使用d3.max/d3.min獲取數組的最大最小值, 也可使用d3.extent獲取數組的最小最大值.
而extent甚至還能夠接收一個對象和函數:
var objects = [ {"num": 1}, {"num": 3}, {"num": 2}, {"num": 4} ]; function value(d) { return d.num; } // [1,4] console.log(d3.extent(objects, value));
對於顏色, 咱們也能夠編寫:
var x = d3.scale.linear() .domain([12, 24]) .range(["steelblue", "brown"]); // #ʚ586 console.log(x(16));
甚至對像素, 咱們也能夠編寫:
var x = d3.scale.linear() .domain([12, 24]) .range(["0px", "720px"]); // 240px console.log(x(16));
咱們可使用interpolate來更改所要顯示的格式:
var x = d3.scale.linear() .domain([12, 24]) .range(["steelblue", "brown"]) .interpolate(d3.interpolateHsl); // #5f3cb0 console.log(x(16));
實際上, 對於domain/range, 不單單隻支持兩個參數, 容許傳遞多個參數:
var x = d3.scale.linear() .domain([-10, 0, 100]) .range(["red", "white", "green"]); console.log(x(-5)); // #ff8080 console.log(x(50)); // #80c080
對於ordinal來講, 其domain和range是顯式一一對應的:
var x = d3.scale.ordinal() .domain(["A", "B", "C", "D"]) .range([0, 10, 20, 30, 40]); console.log(x("B")); // 10 console.log(x("f")); // 40
而ordinal常常用於categorical colors:
var x = d3.scale.category20() .domain(["A", "B", "C", "D"]) console.log(x("B")); // #aec7e8
d3.js提供的顏色:
而對於ordinal來講, 其range會依據domain進行等間距劃分:
var x = d3.scale.ordinal() .domain(["A", "B", "C", "D"]) .rangePoints([0, 720]); console.log(x("B")); // 240
咱們能夠經過給定的scale, 建立一個axis:
var yAxis = d3.svg.axis() .scale(y) .orient("left");
而後將y軸綁定到指定的繪圖區間:
svg.append("g") .attr("class", "y axis") .call(yAxis);
而經過以上代碼生成的html, 實際上大概以下:
由line進行線條的繪製, 而text顯示座標值. 因此咱們一般須要爲line或path進行style的設置:
.axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; }
咱們通常使用transforms進行元素的移動:
後記:
1. HTML學習資料:
https://developers.whatwg.org/
https://developer.mozilla.org/zh-CN/
2. SVG學習資料
https://developer.mozilla.org/en-US/docs/Web/SVG
https://github.com/d3/d3/wiki/SVG-Shapes
3. CSS學習資料
https://www.w3.org/TR/selectors/
4. JavaScript學習資料
https://developer.mozilla.org/en-US/docs/Web/JavaScript
http://javascript.crockford.com/
5. D3學習資料
https://github.com/d3/d3/wiki/API-Reference
https://groups.google.com/forum/#!forum/d3-js
http://stackoverflow.com/questions/tagged/d3.js
6. 本學習資料來自: