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. 本学习资料来自: