D3.js(v3)+react 制作 一个带坐标轴与比例尺的折线图
2024-08-26 20:13:57
本章使用路径生成器来绘制一个折线图。以中国和日本的GDP数据为例:
//数据
var dataList = [
{
coountry : "china",
gdp : [
[2000,11920],[2001,13170],[2002,14550],[2003,16500],[2004,19440],[2005,22870],
[2006,27930],[2007,35040],[2008,45470],[2009,51050],[2010,59490],[2011,73140],
[2012,83860],[2013,103550]
]
},
{
coountry : "japan",
gdp : [
[2000,47310],[2001,41590],[2002,39800],[2003,43020],[2004,46500],[2005,45710],
[2006,43560],[2007,43560],[2008,48490],[2009,50350],[2010,54950],[2011,59050],
[2012,59370],[2013,48980]
]
}
]
dataList是一个数组,每一项是一个对象,对象里有两个成员,国家名称country和国民生产总值GDP。GDP也是一个数组,其中[2000,11920]表示2000年的GDP为11920亿美元。
首先,定义x轴和y轴的比例尺,x轴表示年份,y轴表示GDP值。定义比例尺之前,要明确绘制区域和GDP的最大值:
1 //外边框
var padding = {top : 50 , right : 50 , bottom : 100 , left : 200}; //计算GDP的最大值
var gdpmax = 0;
for (var i = 0; i < dataList.length ; i++){
var currGdp = d3.max(dataList[i].gdp,function(d){
return d[1]
})
if(currGdp > gdpmax){
gdpmax = currGdp
}
}
padding是到SVG画板上下左右各边界的距离,单位为像素。GDP的最大值保存在gdpmax变量中。使用d3.max()可以很方便的求数组中的最大值。接下来,凭借padding和gdpmax定义比例尺的定义域和值域:
//定义比例尺,均为线性比例尺
var xScale = d3.scale.linear() //定义一个比例尺
.domain([min,max]) //设定x轴的值域
.range([0,width-padding.left - padding.right]) //设定x轴的定义域 var yScale = d3.scale.linear() //定义一个比例尺
.domain([0,gdpmax*1.1]) //设定y轴的值域
.range([height-padding.top-padding.bottom,0]) //设定y轴的定义域
x轴的定义域是2000~2013,此外为了代码简洁手动指定了,实际应用时应从数据中获取。y轴的定义域是0~gdpmax*1.1,乘以1.1是为了使得图形不在坐标轴的边界绘制。接下来根据数据定义一个线段生成器:
//创建一个线段生成器
var linePath = d3.svg.line() //创建一个线段生成器 .x(function(d){return xScale(d[0])}) //设置x坐标的访问器
.y(function(d){return yScale(d[1])}) //设置y坐标的访问器
该直线生成器的访问器x为xScale(d[0]),y为yScale(d[1])。接下来要传入的数据是gdp数组,如d为[2000,11920]这样的值:那么d[0]就是年份,d[1]是国民生产总值。对这两个值都使用比例尺变换,则输入的数据会自动按照比例伸缩后再生成直线路径。
定义两个RGB颜色,分别用于两条折现的着色。然后,添加与数组dataList长度相同数量的<path>元素,并设置为线段生成器计算的路径。代码:
//定义两个颜色
var colors = [d3.rgb(0,0,255),d3.rgb(0,255,0)] //添加路径
svg.selectAll("path") //选择svg中所有的path
.data(dataList) //绑定数据
.enter() //获取enter部分
.append("path") //添加足够数量的<path>元素
.attr("transform","translate("+padding.left + "," + padding.top + ")") //平移
.attr("d",function(d){
return linePath(d.gdp) //返回线段生成器得到的路径
})
.attr("fill","none") //填充色为none
.attr("stroke",function(d,i){
return colors[i] //设置折线颜色
})
.attr("stroke-width","3px") //设置折线的宽度
添加元素的形式"selectAll().data().enter().append()",相信大家都已经很熟悉了。给属性transform赋予适当的值,令折线平移到指定的位置。在<path>元素的d属性中,使用线段生成器计算路径,注意linePath()的参数是d.gdp。此处的线段生成器是按照数组gdp的格式来设定访问器的,因此一定要以d.gdp,而不是以d作为参数。
接下来绘制坐标轴:
//坐标轴x轴
var xAxis = d3.svg.axis() //创建一个新坐标轴
.scale(xScale) //设定x坐标轴的比例尺
.ticks(6) //设定x坐标轴的分隔数
.tickFormat(d3.format("d")) //刻度的数组用字符串表示
.orient("bottom") //设定x坐标轴的方向
//坐标轴y轴
var yAxis = d3.svg.axis() //创建一个新坐标
.scale(yScale) //设定y坐标轴的比例尺
.orient("left") //设定y坐标轴的方向 //添加一个<g>元素用于放x轴
svg.append("g") //添加一个<g>元素
.attr("class","axis") //定义class名
.attr("transform","translate("+padding.left + "," + (height-padding.bottom) + ")") //平移
.call(xAxis) //call()应用 //添加一个<g>元素用于放y轴
svg.append("g") //添加一个<g>元素
.attr("class","axis") //定义class名
.attr("transform","translate("+ padding.left + "," + padding.top + ")") //平移
.call(yAxis) //call()应用
由于x轴的刻度是年份的意思,而数据里的数据类型确实整数类型:所以如果直接在坐标轴上显示,2000年会显示成2,000, 2002年会显示成2,002多一个逗号。因此,加了一条tickFormat(),其中,d3.format("d")表示刻度的数组都用字符串表示。设定之后,年份之间的逗号就会消失。然后,将两个坐标轴分别放到两个<g>元素里。
现在的效果图如下:
如果想要使一段一段的直线看起来更光滑一些,可以使用直线生成器的插值函数。之前给大家介绍过,不清楚的点: https://www.cnblogs.com/littleSpill/p/10850364.html 设置为basis模式后,线段变为曲线。
//创建一个线段生成器
var linePath = d3.svg.line() //创建一个线段生成器
.interpolate("basis") //使用basis插值模式
.x(function(d){return xScale(d[0])}) //设置x坐标的访问器
.y(function(d){return yScale(d[1])}) //设置y坐标的访问器
效果图:
上面的折线图还缺少一个标记,用户不知道哪条直线是中国的GDP,哪条是日本的GDP。可添加两个矩形,分别填充为相应的颜色。矩形边上添加表示国家名称的文字。
//添加两个矩形标记
var g = svg.selectAll("rect") //将选择集赋值给变量g
.data(dataList) //绑定数据
.enter() //获取enter()部分
.append("g") //添加<g>元素
g.append("rect") //在<g>元素里添加<rect>矩形
.attr("fill",function(d,i){ //设定颜色
return colors[i]
})
.attr("transform",function(d,i){ //平移
var x = padding.left + i*150
var y = height - padding.bottom + 50
return "translate(" +x + "," + y + ")"
})
.attr("width",20) //设定矩形的宽度
.attr("height",20) //设定矩形的高度 //添加注解
g.append("text") //添加文字
.attr("class","text") //定义class名
.attr("x",function(d,i){ //设定文字在x方向的位置
return padding.left + i * 150 + 30
})
.attr("y",function(d,i){ //设定文字在y方向的位置
return height - padding.bottom + 50 + 15
})
.text(function(d){ //设定文字的内容
return d.coountry
})
.attr("font-size","15px") //设定文字的大小
.attr("fill","black") //设定文字的颜色
如下图: 一个完整的折线图就做好了。
完整代码:
import React, { Component } from 'react';
import * as d3 from 'd3'
class Line extends Component {
constructor(props) {
super(props);
this.state = {}
} componentDidMount(){
this.oneMethod()
} oneMethod(){ var width = 800; //SVG绘制区域的宽度
var height = 600; //SVG绘制区域的高度 var svg = d3.select("#body") //选择id为body的div
.append("svg") //在div中添加<svg>
.attr("width",width) //设定<svg>的宽度
.attr("height",height) //设定<svg>的高度 //数据
var dataList = [
{
coountry : "china",
gdp : [
[2000,11920],[2001,13170],[2002,14550],[2003,16500],[2004,19440],[2005,22870],
[2006,27930],[2007,35040],[2008,45470],[2009,51050],[2010,59490],[2011,73140],
[2012,83860],[2013,103550]
]
},
{
coountry : "japan",
gdp : [
[2000,47310],[2001,41590],[2002,39800],[2003,43020],[2004,46500],[2005,45710],
[2006,43560],[2007,43560],[2008,48490],[2009,50350],[2010,54950],[2011,59050],
[2012,59370],[2013,48980]
]
}
] //外边框
var padding = {top : 50 , right : 50 , bottom : 100 , left : 200}; //计算GDP的最大值
var gdpmax = 0;
for (var i = 0; i < dataList.length ; i++){
var currGdp = d3.max(dataList[i].gdp,function(d){
return d[1]
})
if(currGdp > gdpmax){
gdpmax = currGdp
}
} //先选出年份的最小值与最大值
for (var i = 0; i < dataList.length ; i++){
var min = d3.min(dataList[i].gdp,function(d){return d[0]})
var max = d3.max(dataList[i].gdp,function(d){return d[0]})
}
//定义比例尺,均为线性比例尺
var xScale = d3.scale.linear() //定义一个比例尺
.domain([min,max]) //设定x轴的值域
.range([0,width-padding.left - padding.right]) //设定x轴的定义域 var yScale = d3.scale.linear() //定义一个比例尺
.domain([0,gdpmax*1.1]) //设定y轴的值域
.range([height-padding.top-padding.bottom,0]) //设定y轴的定义域
//创建一个线段生成器
var linePath = d3.svg.line() //创建一个线段生成器
.interpolate("basis") //使用basis插值模式
.x(function(d){return xScale(d[0])}) //设置x坐标的访问器
.y(function(d){return yScale(d[1])}) //设置y坐标的访问器 //定义两个颜色
var colors = [d3.rgb(0,0,255),d3.rgb(0,255,0)] //添加路径
svg.selectAll("path") //选择svg中所有的path
.data(dataList) //绑定数据
.enter() //获取enter部分
.append("path") //添加足够数量的<path>元素
.attr("transform","translate("+padding.left + "," + padding.top + ")") //平移
.attr("d",function(d){
return linePath(d.gdp) //返回线段生成器得到的路径
})
.attr("fill","none") //填充色为none
.attr("stroke",function(d,i){
return colors[i] //设置折线颜色
})
.attr("stroke-width","3px") //设置折线的宽度 //坐标轴x轴
var xAxis = d3.svg.axis() //创建一个新坐标轴
.scale(xScale) //设定x坐标轴的比例尺
.ticks(6) //设定x坐标轴的分隔数
.tickFormat(d3.format("d")) //刻度的数组用字符串表示
.orient("bottom") //设定x坐标轴的方向
//坐标轴y轴
var yAxis = d3.svg.axis() //创建一个新坐标
.scale(yScale) //设定y坐标轴的比例尺
.orient("left") //设定y坐标轴的方向 //添加一个<g>元素用于放x轴
svg.append("g") //添加一个<g>元素
.attr("class","axis") //定义class名
.attr("transform","translate("+padding.left + "," + (height-padding.bottom) + ")") //平移
.call(xAxis) //call()应用 //添加一个<g>元素用于放y轴
svg.append("g") //添加一个<g>元素
.attr("class","axis") //定义class名
.attr("transform","translate("+ padding.left + "," + padding.top + ")") //平移
.call(yAxis) //call()应用 //添加两个矩形标记
var g = svg.selectAll("rect") //将选择集赋值给变量g
.data(dataList) //绑定数据
.enter() //获取enter()部分
.append("g") //添加<g>元素
g.append("rect") //在<g>元素里添加<rect>矩形
.attr("fill",function(d,i){ //设定颜色
return colors[i]
})
.attr("transform",function(d,i){ //平移
var x = padding.left + i*150
var y = height - padding.bottom + 50
return "translate(" +x + "," + y + ")"
})
.attr("width",20) //设定矩形的宽度
.attr("height",20) //设定矩形的高度 //添加注解
g.append("text") //添加文字
.attr("class","text") //定义class名
.attr("x",function(d,i){ //设定文字在x方向的位置
return padding.left + i * 150 + 30
})
.attr("y",function(d,i){ //设定文字在y方向的位置
return height - padding.bottom + 50 + 15
})
.text(function(d){ //设定文字的内容
return d.coountry
})
.attr("font-size","15px") //设定文字的大小
.attr("fill","black") //设定文字的颜色 } render() {
return (
<div id="body" > </div>
);
}
} export default Line;
最新文章
- VMware下利用ubuntu13.04建立嵌入式开发环境之四
- SDDC-SDN-SDS
- cs11_c++_lab5待修改
- hdu 5751 Eades
- 安卓发展史以及安卓和苹果对比PPT
- Python内置数据类型之Tuple篇
- linux内核分析之进程地址空间管理
- Swift - 使用CoreLocation获取设备方向(真实方向,磁极方向)
- LDAPserver的安装
- $pull
- fragment 数据传递,传值,通信
- vsftpd3.0之匿名用户配置
- Spark:性能调优
- curl支持HTTP和https
- RHEL7.0 Docker离线安装以及实战笔记
- java类定义、变量类型、构造函数
- JS原型--原型链
- spring学习总结——装配Bean学习二(JavaConfig装配bean)
- kibana连接elasticsearch集群做负载均衡
- 入门Spring ioc
热门文章
- Jenkins 搭建
- vuex 介绍
- storm是怎样保证at least once语义的
- Java Virtual Machine (JVM) objects 虚拟机实例的产生 退出 两种线程
- Redis persistence demystified
- Kafka 配置参数汇总及相关说明
- ​vmware虚拟机centos网络配置错误,执行/etc/init.d/network start 或 restart 提示Device eth0 has different MAC address than expected, ignoring
- SPOJ7258
- html标签默认属性值之margin;padding值
- UVALive - 4867 —— dp