(1)设置数轴
D3的数轴实际商是由程序员自己来定义参数的函数。调用数轴函数,会生成数轴相关的可见元素,包括轴线、标签和刻度 。
使用d3.svg.axis()能创建通用的数轴函数:
var xAxis = d3.svg.axis();
但是你要注意,在使用之前你要告诉这个函数,是基于什么比例尺工作的。例如序数比例尺。
同时,你可以设置标签相对数轴显示的位置,默认出现在轴线的下方。通常而言,水平数轴的位置,可放置在顶部或底部,垂直数轴则要么放在左或者右。
var Axis = d3.svg.axis() .scale(xScale) .orient("bottom");
最后你想要把实际生成的数轴,将其线条和标签插入到SVG中,必须调用xAxis函数。
svg.append("g").call(xAxis);
//在svg标签内,g元素就是一个分组元素。分组元素是不可见的,跟line,rect和circle不一样,但它有两大用途:一是用来包含其他元素;二是对整个分组应用变换,从而影响到该组中所有元素。
//call()在D3中会取得传递过来的元素,然后再把它交给其他函数。对这个例子而言,传递过来的元素就hi新的分组元素g。而call()接着把g交给了xAxis函数,即在g元素内生成数轴。
(2)修整数轴
上面的情况,我们还无法给新创建的g元素赋予样式。
那该怎么做呢?通常情况下,我们可以给g元素指定一个axis类。
svg.append("g") .attr("class","axis") .call(xAxis); 然后写下样式: .axis path, .axis line { fill: none; stroke:black; shape-rendering:crispEdges; } .axis text { font-size:11px; }
于是,就这样我们把所有的数轴元素都放在一个g分组中,能够使用CSS选择符.axis 为其中的任何元素应用样式。
从上面的样式可见,数轴本身是由path,line,和text元素组成的。
但是,要注意的是,在给SVG元素应用样式时,要确保应用的属性名是SVG的,而不是CSS的。(SVG属性名参考:https://developer.mozilla.org/en-US/docs/SVG/Attribute)
但是,我们看到这个数轴是在上方。按常理,不是都应该在下面的吗?
此时,我们可以通过SVG变换:
svg.append("g") .attr("class","axis") .attr("transform","translate(0,"+(h-padding)+")") .call(xAxis); //translate(x,y)这是一个平移变换,上述代码中只是平移了y轴,x轴不变。(h-padding)是把分组的顶边y坐标设置为h,即整个SVG元素的高度,然后再减去我们前面定义的边距值(padding).
我们看到,g元素被加上了一个transform属性。
另外,如果你觉得数轴上的刻度线有些多的话,你还能设置设置刻度线的数量:
在定义数轴时,使用ticks(num)函数,设置数量值。如图:
##(3)垂直数轴
相比水平数轴xAxis,我们可以通过修改其代码,相对于yScale比例尺而定义一个y轴。
let yAxis = d3.svg.axis() .scale(yScale) .orient("left") .ticks(5); svg.append("g").attr("class","axis").attr("transform","translate("+padding+",0)").call(yAxis);
##(4)优化
为了证明新坐标轴是可动态伸缩的,我们把静态的数据集改成随机生成的:
let dataset = []; let numDataPoints = 50; let xRange = Math.random() * 1000; let yRange = Math.random() * 1000; for(let i = 0;i<numDataPoints;i++) { let newNumber1 = Math.floor(Math.random()* xRange); let newNumber2 = Math.floor(Math.random()* yRange); dataset.push([newNumber1,newNumber2]);//初始化随机数据集 }
现在我们在来刷新下页面,你会发现每次刷新,都会生成不同的数据集。但是,你也看到数轴会随着输入值域的变化而相应地缩放,刻度和标签也会相应地变化。
另外,我们也可以会刻度上的标签定义样式。利用tickFormat()能为数值应用不同的格式,例如数值保留小数后三位,或显示为百分比等等。如,数值为0.23返回的是23%
但是,使用tickFormat()之前,首先要定义一个新的数值格式函数。通过这个函数可以告诉D3把数值当成百分比,同时保留一位小数等等。
let formatAsPercentage = d3.format(".1%"); xAxis.tickFormat(formatAsPercentage);
##(5)附完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> div.bar { display: inline-block; width: 20px; height: 75px; margin-right: 2px; background-color: teal; } .axis path, .axis line { fill: none; stroke:black; shape-rendering:crispEdges; } .axis text { font-size:11px; } </style> </head> <body> <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.js"></script> <script src="https://d3js.org/d3.v3.js"></script> <script> //D3.js code let w = 600; let h = 200; let padding = 30; let svg = d3.select("body").append("svg").attr("width",w).attr("height",h);//把append()返回的新元素保存在了变量svg中 // let dataset = [ // [5,20],[480,90],[250,50],[100,33],[330,95],[410,12],[475,44],[25,67],[85,21],[220,88] // ]; let dataset = []; let numDataPoints = 50; let xRange = Math.random() * 1000; let yRange = Math.random() * 1000; for(let i = 0;i<numDataPoints;i++) { let newNumber1 = Math.floor(Math.random()* xRange); let newNumber2 = Math.floor(Math.random()* yRange); dataset.push([newNumber1,newNumber2]);//初始化随机数据集 } let xScale = d3.scale.linear() .domain([0,d3.max(dataset,function(d){return d[0];})]) .range([padding,w-padding*2]) .nice();//nice()告诉比例尺取得为range()设置的任何值域,把两端的值扩展到最接近的整数。如[0.2000011166,0.99999943]优化为[0.2,1] let yScale = d3.scale.linear() .domain([0,d3.max(dataset,function(d){return d[1];})]) .range([h-padding,padding]) .nice(); let rScale = d3.scale.linear() .domain([0,d3.max(dataset,function(d){return d[1];})]) .range([2,5]) .nice(); // 数轴 let xAxis = d3.svg.axis() .scale(xScale) .orient("bottom") .ticks(5); let yAxis = d3.svg.axis() .scale(yScale) .orient("left") .ticks(5); svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx",function(d,i){ return xScale(d[0]); //返回缩放后的值 }) .attr("cy",function(d){ return yScale(d[1]); }) .attr("r",function(d){ return rScale(d[1]); }); //添加标签 // svg.selectAll("text") // .data(dataset) // .enter() // .append('text') // .text(function(d){ // return d[0]+ "," + d[1];//设置标签内容 // }) // .attr({ // fill : "black", // x : function(d) {return xScale(d[0])+10;},//将标签与散点位置一一对应 // y : function(d) {return yScale(d[1]);} // }) // .style("font-size", "11px"); //添加数轴 svg.append("g") .attr("class","axis") .attr("transform","translate(0,"+(h-padding)+")") .call(xAxis); svg.append("g") .attr("class","axis") .attr("transform","translate("+padding+",0)") .call(yAxis); </script> </body>