【新东网技术大咖带你走进D3】神奇的D3是个什么玩意儿
发布时间: 2017-01-13 11:31:14
文/李壮相 技术总监
什么是D3?D3是指数据驱动文档(Data-Driven Documents),根据D3的官方定义:
D3.js是一个JavaScript库,它可以通过数据来操作文档。D3可以通过使用HTML、SVG和CSS把数据鲜活形象地展现出来。D3严格遵循Web标准,因而可以让你的程序轻松兼容现代主流浏览器并避免对特定框架的依赖。同时,它提供了强大的可视化组件,可以让使用者以数据驱动的方式去操作DOM。(来自:维基百科D3.js词条) |
总的来说,D3是这样一个特殊的JavaScript库,它利用现有的Web标准,通过更简单的(数据驱动)方式来制作炫目的可视化效果。
你可以从官方网站下载一个在http://d3js.org/下载最新版本的D3.js,然后根据官方的说明,你可以搭建一个D3的开发环境,我就不细说了。有意思的是,它对外界的其他JS库,是零依赖的。
D3能做的事情是比较多的,能做很多形形色色的数据驱动的图例,我们可以通过访问以下网址获得大量的样例,连同没有图示的,高达数千种:
https://github.com/d3/d3/wiki/Gallery
注意看,你会发现很多图例甚至包含了强大的动画效果图例,是为其强大之处。
我们可以通过一个非常简单的饼状图来做一个效果还不错的案例:
以下是部分代码:
var salesData=[
{label:"Basic", color:"#3366CC"},
{label:"Plus", color:"#DC3912"},
{label:"Lite", color:"#FF9900"},
{label:"Elite", color:"#109618"},
{label:"Delux", color:"#990099"}
];
var svg = d3.select("body").append("svg").attr("width",700).attr("height",300);
svg.append("g").attr("id","salesDonut");
svg.append("g").attr("id","quotesDonut");
Donut3D.draw("salesDonut", randomData(), 150, 150, 130, 100, 30, 0.4);
Donut3D.draw("quotesDonut", randomData(), 450, 150, 130, 100, 30, 0);
function changeData(){
Donut3D.transition("salesDonut", randomData(), 130, 100, 30, 0.4);
Donut3D.transition("quotesDonut", randomData(), 130, 100, 30, 0);
}
function randomData(){
return salesData.map(function(d){
return {label:d.label, value:1000*Math.random(), color:d.color};});
}
其中Donut3D.js的问题内容如下:
!function(){ var Donut3D={};
function pieTop(d, rx, ry, ir ){ if(d.endAngle - d.startAngle == 0 ) return "M 0 0"; var sx = rx*Math.cos(d.startAngle), sy = ry*Math.sin(d.startAngle), ex = rx*Math.cos(d.endAngle), ey = ry*Math.sin(d.endAngle);
var ret =[]; ret.push("M",sx,sy,"A",rx,ry,"0",(d.endAngle-d.startAngle > Math.PI? 1: 0),"1",ex,ey,"L",ir*ex,ir*ey); ret.push("A",ir*rx,ir*ry,"0",(d.endAngle-d.startAngle > Math.PI? 1: 0), "0",ir*sx,ir*sy,"z"); return ret.join(" "); }
function pieOuter(d, rx, ry, h ){ var startAngle = (d.startAngle > Math.PI ? Math.PI : d.startAngle); var endAngle = (d.endAngle > Math.PI ? Math.PI : d.endAngle);
var sx = rx*Math.cos(startAngle), sy = ry*Math.sin(startAngle), ex = rx*Math.cos(endAngle), ey = ry*Math.sin(endAngle);
var ret =[]; ret.push("M",sx,h+sy,"A",rx,ry,"0 0 1",ex,h+ey,"L",ex,ey,"A",rx,ry,"0 0 0",sx,sy,"z"); return ret.join(" "); }
function pieInner(d, rx, ry, h, ir ){ var startAngle = (d.startAngle < Math.PI ? Math.PI : d.startAngle); var endAngle = (d.endAngle < Math.PI ? Math.PI : d.endAngle);
var sx = ir*rx*Math.cos(startAngle), sy = ir*ry*Math.sin(startAngle), ex = ir*rx*Math.cos(endAngle), ey = ir*ry*Math.sin(endAngle);
var ret =[]; ret.push("M",sx, sy,"A",ir*rx,ir*ry,"0 0 1",ex,ey, "L",ex,h+ey,"A",ir*rx, ir*ry,"0 0 0",sx,h+sy,"z"); return ret.join(" "); }
function getPercent(d){ return (d.endAngle-d.startAngle > 0.2 ? Math.round(1000*(d.endAngle-d.startAngle)/(Math.PI*2))/10+'%' : ''); }
Donut3D.transition = function(id, data, rx, ry, h, ir){ function arcTweenInner(a) { var i = d3.interpolate(this._current, a); this._current = i(0); return function(t) { return pieInner(i(t), rx+0.5, ry+0.5, h, ir); }; } function arcTweenTop(a) { var i = d3.interpolate(this._current, a); this._current = i(0); return function(t) { return pieTop(i(t), rx, ry, ir); }; } function arcTweenOuter(a) { var i = d3.interpolate(this._current, a); this._current = i(0); return function(t) { return pieOuter(i(t), rx-.5, ry-.5, h); }; } function textTweenX(a) { var i = d3.interpolate(this._current, a); this._current = i(0); return function(t) { return 0.6*rx*Math.cos(0.5*(i(t).startAngle+i(t).endAngle)); }; } function textTweenY(a) { var i = d3.interpolate(this._current, a); this._current = i(0); return function(t) { return 0.6*rx*Math.sin(0.5*(i(t).startAngle+i(t).endAngle)); }; }
var _data = d3.layout.pie().sort(null).value(function(d) {return d.value;})(data);
d3.select("#"+id).selectAll(".innerSlice").data(_data) .transition().duration(750).attrTween("d", arcTweenInner);
d3.select("#"+id).selectAll(".topSlice").data(_data) .transition().duration(750).attrTween("d", arcTweenTop);
d3.select("#"+id).selectAll(".outerSlice").data(_data) .transition().duration(750).attrTween("d", arcTweenOuter);
d3.select("#"+id).selectAll(".percent").data(_data).transition().duration(750) .attrTween("x",textTweenX).attrTween("y",textTweenY).text(getPercent); }
Donut3D.draw=function(id, data, x /*center x*/, y/*center y*/, rx/*radius x*/, ry/*radius y*/, h/*height*/, ir/*inner radius*/){
var _data = d3.layout.pie().sort(null).value(function(d) {return d.value;})(data);
var slices = d3.select("#"+id).append("g").attr("transform", "translate(" + x + "," + y + ")") .attr("class", "slices");
slices.selectAll(".innerSlice").data(_data).enter().append("path").attr("class", "innerSlice") .style("fill", function(d) { return d3.hsl(d.data.color).darker(0.7); }) .attr("d",function(d){ return pieInner(d, rx+0.5,ry+0.5, h, ir);}) .each(function(d){this._current=d;});
slices.selectAll(".topSlice").data(_data).enter().append("path").attr("class", "topSlice") .style("fill", function(d) { return d.data.color; }) .style("stroke", function(d) { return d.data.color; }) .attr("d",function(d){ return pieTop(d, rx, ry, ir);}) .each(function(d){this._current=d;});
slices.selectAll(".outerSlice").data(_data).enter().append("path").attr("class", "outerSlice") .style("fill", function(d) { return d3.hsl(d.data.color).darker(0.7); }) .attr("d",function(d){ return pieOuter(d, rx-.5,ry-.5, h);}) .each(function(d){this._current=d;});
slices.selectAll(".percent").data(_data).enter().append("text").attr("class", "percent") .attr("x",function(d){ return 0.6*rx*Math.cos(0.5*(d.startAngle+d.endAngle));}) .attr("y",function(d){ return 0.6*ry*Math.sin(0.5*(d.startAngle+d.endAngle));}) .text(getPercent).each(function(d){this._current=d;}); }
this.Donut3D = Donut3D; }();
|
通过这个案例,你会发现,其实D3需要一定的数学功底,当然有幸的,D3的官网提供了大量案例,你可以直接调用就可以了。