D3.js学习笔记五:为折线图添加标题和网格线

2015年02月11日 javascript 暂无评论 阅读 2,652 views 次

要解决的问题

现在这个统计图还要解决几个问题:为图表加入标题、作图区的网格线、支持多个系列、为多系列加入图例。

为了添加标题和图例,我们需要修改作图区的大小,为它们预留位置,如果没有这些元素的话,还需要将这些保留的位置变为作图区。添加多系列并且支持动画,需要解决数据系列长度前后不一致导致的顶点添加和删除的问题。

为图表添加标题

为图表插入标题和副标题很简单,我们只需要为它添加两个text对象就可以了,还需要调整纵坐标轴,使它的显示范围为现有高度减去标题的高度。为了方便调整,我们使用CSS来控制标题和副标题的外观。

.title{font-family:Arial,微软雅黑;font-size:18px;text-anchor:middle;} .subTitle{font-family:Arial,宋体;font-size:12px;text-anchor:middle;fill:#666}

如果用户没有为折线图输入标题,我们保持图表为原样,否则加入标题,为此,我们需要一个变量保存现在的顶部高度,以灵活的应对用户自定义标题。顺便定义一个变量存储底部的高度,如果用户的系列超过1个,我们预留出位置显示图例。

var head_height=padding; var title="收支平衡统计图"; var subTitle="2013年1月 至 2013年6月"; var foot_height=padding;

然后我们添加标题和副标题并记录现在的顶部高度,其中.attr("class","title")指定了标题的样式,这样等我们以后需要修改样式的时候,直接改样式表就行了。.attr("y",head_height)设定当前文本的Y坐标就是当前的head_height。

//添加标题 if(title!="") { svg.append("g") .append("text") .text(title) .attr("class","title") .attr("x",w/2) .attr("y",head_height); head_height+=30; } //添加副标题 if(subTitle!="") { svg.append("g") .append("text") .text(subTitle) .attr("class","subTitle") .attr("x",w/2) .attr("y",head_height); head_height+=20; }

因为加了标题和副标题,作图区高度变小,因此需要更改纵坐标的显示范围range,要注意的是,这个修改同样要在drawChart()函数里面做一次。

var yScale = d3.scale.linear() .domain([0,d3.max(dataset)]) .range([h-foot_height,head_height]);

现在,统计图已经有了标题和副标题,如果我们将其值设置为空,则纵坐标轴会自动缩放。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>画一个折线图</title>
<script type="text/javascript" src="js/d3.js"></script>
</head>
<style type="text/css">
body{
height: 100%;
}
.title{font-family:Arial,微软雅黑;font-size:18px;text-anchor:middle;}
.subTitle{font-family:Arial,宋体;font-size:12px;text-anchor:middle;fill:#666}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
fill:#999;
}
</style>
<body>
<script type="text/javascript">
var dataset=[];
var xMarks=[];
var w=600;
var h=400;
var padding=40;
//用一个变量存储标题和副标题的高度,如果没有标题什么的,就为0
var head_height=padding;
var title="收支平衡统计图";
var subTitle="2013年1月 至 2013年6月";
//用一个变量计算底部的高度,如果不是多系列,就为0
var foot_height=padding;
//模拟数据
getData();
//定义画布
var svg=d3.select("body")
.append("svg")
.attr("width",w)
.attr("height",h);
//添加背景
svg.append("g")
.append("rect")
.attr("x",0)
.attr("y",0)
.attr("width",w)
.attr("height",h)
.style("fill","#FFF")
.style("stroke-width",2)
.style("stroke","#E7E7E7");
//添加标题
if(title!="")
{
svg.append("g")
.append("text")
.text(title)
.attr("class","title")
.attr("x",w/2)
.attr("y",head_height);
head_height+=30;
}
//添加副标题
if(subTitle!="")
{
svg.append("g")
.append("text")
.text(subTitle)
.attr("class","subTitle")
.attr("x",w/2)
.attr("y",head_height);
head_height+=20;
}
//横坐标轴比例尺
var xScale = d3.scale.linear()
.domain([0,dataset.length-1])
.range([padding,w-padding]);
//纵坐标轴比例尺
var yScale = d3.scale.linear()
.domain([0,d3.max(dataset)])
.range([h-foot_height,head_height]);
//定义横轴
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom").ticks(dataset.length);
//添加横坐标轴并通过编号获取对应的横轴标签
var xBar=svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis)
.selectAll("text")
.text(function(d){return xMarks[d];});
//定义纵轴
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left").ticks(10);
//添加纵轴
var yBar=svg.append("g")
.attr("class", "axis")
.attr("transform", "translate("+padding+",0)")
.call(yAxis);
//添加折线
var line = d3.svg.line()
.x(function(d,i){return xScale(i);})
.y(function(d){return yScale(d);});
var path=svg.append("path")
.attr("d", line(dataset))
.style("fill","#F00")
.style("fill","none")
.style("stroke-width",1)
.style("stroke","#09F")
.style("stroke-opacity",0.9);
//添加系列的小圆点
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d,i) {
return xScale(i);
})
.attr("cy", function(d) {
return yScale(d);
})
.attr("r",5)
.attr("fill","#09F");
//重新作图
function drawChart()
{
getData();
yBar.transition().duration(1000).call(yAxis);
//纵轴数据更新
yScale = d3.scale.linear()
.domain([0,d3.max(dataset)])
.range([h-padding,head_height]);
//重绘路径
path.transition().duration(1000).attr("d", line(dataset));
//重绘4圆点
svg.selectAll("circle")
.data(dataset)
.transition()
.duration(1000)
.attr("cx", function(d,i) {
return xScale(i);
})
.attr("cy", function(d) {
return yScale(d);
})
}
//产生随机数据
function getData()
{
var dataNum=15;
dataset=[];
xMarks=[];
for(i=1;i<dataNum;i++)
{
dataset.push(Math.round(Math.random()*h));
xMarks.push("标签"+i);
}
}
</script>
<p align="left">
<button onClick="javascript:drawChart();">刷新数据</button>
</p>
</body>
</html>

为图表添加网格线

为图表插入网格线很简单,就是生成两个标记尺寸为背景大小的axis组件,然后和横纵轴重叠就可以了。首先定义内层网格线的样式:

.inner_line path, .inner_line line { fill: none; stroke:#E7E7E7; shape-rendering: crispEdges; }

然后添加如下代码,加入标记,主要是设置.tickSize属性,更改标记尺寸,tickFormat("")使它不显示标签,然后在添加的时候更改其样式。

//定义横轴网格线 var xInner = d3.svg.axis() .scale(xScale) .tickSize(-(h-head_height-foot_height),0,0) .orient("bottom") .ticks(dataset.length); //添加横轴网格线 svg.append("g") .attr("class","inner_line") .attr("transform", "translate(0," + (h - padding) + ")") .call(xInner) .selectAll("text") .text(""); //定义纵轴网格线 var yInner = d3.svg.axis() .scale(yScale) .tickSize(-(w-padding*2),0,0) .tickFormat("") .orient("left") .ticks(10); //添加纵轴网格线 var yBar=svg.append("g") .attr("class", "inner_line") .attr("transform", "translate("+padding+",0)") .call(yInner);

 

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>画一个折线图</title>
<script type="text/javascript" src="js/d3.js"></script>
</head>
<style type="text/css">
body{
height: 100%;
}
.title{font-family:Arial,微软雅黑;font-size:18px;text-anchor:middle;}
.subTitle{font-family:Arial,宋体;font-size:12px;text-anchor:middle;fill:#666}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
fill:#999;
}
.inner_line path,
.inner_line line {
fill: none;
stroke:#E7E7E7;
shape-rendering: crispEdges;
}
</style>
<body>
<script type="text/javascript">
var dataset=[];
var xMarks=[];
var w=600;
var h=400;
var padding=40;
//用一个变量存储标题和副标题的高度,如果没有标题什么的,就为0
var head_height=padding;
var title="收支平衡统计图";
var subTitle="2013年1月 至 2013年6月";
//用一个变量计算底部的高度,如果不是多系列,就为0
var foot_height=padding;
//模拟数据
getData();
//定义画布
var svg=d3.select("body")
.append("svg")
.attr("width",w)
.attr("height",h);
//添加背景
svg.append("g")
.append("rect")
.attr("x",0)
.attr("y",0)
.attr("width",w)
.attr("height",h)
.style("fill","#FFF")
.style("stroke-width",2)
.style("stroke","#E7E7E7");
//添加标题
if(title!="")
{
svg.append("g")
.append("text")
.text(title)
.attr("class","title")
.attr("x",w/2)
.attr("y",head_height);
head_height+=30;
}
//添加副标题
if(subTitle!="")
{
svg.append("g")
.append("text")
.text(subTitle)
.attr("class","subTitle")
.attr("x",w/2)
.attr("y",head_height);
head_height+=20;
}
//横坐标轴比例尺
var xScale = d3.scale.linear()
.domain([0,dataset.length-1])
.range([padding,w-padding]);
//纵坐标轴比例尺
var yScale = d3.scale.linear()
.domain([0,d3.max(dataset)])
.range([h-foot_height,head_height]);
//定义横轴网格线
var xInner = d3.svg.axis()
.scale(xScale)
.tickSize(-(h-head_height-foot_height),0,0)
.orient("bottom")
.ticks(dataset.length);
//添加横轴网格线
svg.append("g")
.attr("class","inner_line")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xInner)
.selectAll("text")
.text("");
//定义纵轴网格线
var yInner = d3.svg.axis()
.scale(yScale)
.tickSize(-(w-padding*2),0,0)
.tickFormat("")
.orient("left")
.ticks(10);
//添加纵轴网格线
var yBar=svg.append("g")
.attr("class", "inner_line")
.attr("transform", "translate("+padding+",0)")
.call(yInner);
//定义横轴
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(dataset.length);
//添加横坐标轴并通过编号获取对应的横轴标签
var xBar=svg.append("g")
.attr("class","axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis)
.selectAll("text")
.text(function(d){return xMarks[d];});
//定义纵轴
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(10);
//添加纵轴
var yBar=svg.append("g")
.attr("class", "axis")
.attr("transform", "translate("+padding+",0)")
.call(yAxis);
//添加折线
var line = d3.svg.line()
.x(function(d,i){return xScale(i);})
.y(function(d){return yScale(d);});
var path=svg.append("path")
.attr("d", line(dataset))
.style("fill","#F00")
.style("fill","none")
.style("stroke-width",1)
.style("stroke","#09F")
.style("stroke-opacity",0.9);
//添加系列的小圆点
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d,i) {
return xScale(i);
})
.attr("cy", function(d) {
return yScale(d);
})
.attr("r",5)
.attr("fill","#09F");
//重新作图
function drawChart()
{
getData();
yBar.transition().duration(1000).call(yAxis);
//纵轴数据更新
yScale = d3.scale.linear()
.domain([0,d3.max(dataset)])
.range([h-padding,head_height]);
//重绘路径
path.transition().duration(1000).attr("d", line(dataset));
//重绘4圆点
svg.selectAll("circle")
.data(dataset)
.transition()
.duration(1000)
.attr("cx", function(d,i) {
return xScale(i);
})
.attr("cy", function(d) {
return yScale(d);
})
}
//产生随机数据
function getData()
{
var dataNum=15;
dataset=[];
xMarks=[];
for(i=1;i<dataNum;i++)
{
dataset.push(Math.round(Math.random()*h));
xMarks.push("标签"+i);
}
}
</script>
<p align="left">
<button onClick="javascript:drawChart();">刷新数据</button>
</p>
</body>
</html>

 

给我留言

您必须 登录 才能发表留言!

Copyright © 大一网 保留所有权利.  

用户登录

分享到: