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元素内生成数轴。
上面的情况,我们还无法给新创建的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>