D3.js(v3)+react 制作 一个带坐标轴与比例尺的折线图
程序员文章站
2022-05-29 12:42:10
本章使用路径生成器来绘制一个折线图。以中国和日本的GDP数据为例: 1 //数据 2 var dataList = [ 3 { 4 coountry : "china", 5 gdp : [ 6 [2000,11920],[2001,13170],[2002,14550],[2003,16500], ......
本章使用路径生成器来绘制一个折线图。以中国和日本的gdp数据为例:
1 //数据 2 var datalist = [ 3 { 4 coountry : "china", 5 gdp : [ 6 [2000,11920],[2001,13170],[2002,14550],[2003,16500],[2004,19440],[2005,22870], 7 [2006,27930],[2007,35040],[2008,45470],[2009,51050],[2010,59490],[2011,73140], 8 [2012,83860],[2013,103550] 9 ] 10 }, 11 { 12 coountry : "japan", 13 gdp : [ 14 [2000,47310],[2001,41590],[2002,39800],[2003,43020],[2004,46500],[2005,45710], 15 [2006,43560],[2007,43560],[2008,48490],[2009,50350],[2010,54950],[2011,59050], 16 [2012,59370],[2013,48980] 17 ] 18 } 19 ]
datalist是一个数组,每一项是一个对象,对象里有两个成员,国家名称country和国民生产总值gdp。gdp也是一个数组,其中[2000,11920]表示2000年的gdp为11920亿美元。
首先,定义x轴和y轴的比例尺,x轴表示年份,y轴表示gdp值。定义比例尺之前,要明确绘制区域和gdp的最大值:
1 //外边框 2 var padding = {top : 50 , right : 50 , bottom : 100 , left : 200}; 3 4 //计算gdp的最大值 5 var gdpmax = 0; 6 for (var i = 0; i < datalist.length ; i++){ 7 var currgdp = d3.max(datalist[i].gdp,function(d){ 8 return d[1] 9 }) 10 if(currgdp > gdpmax){ 11 gdpmax = currgdp 12 } 13 }
padding是到svg画板上下左右各边界的距离,单位为像素。gdp的最大值保存在gdpmax变量中。使用d3.max()可以很方便的求数组中的最大值。接下来,凭借padding和gdpmax定义比例尺的定义域和值域:
1 //定义比例尺,均为线性比例尺 2 var xscale = d3.scale.linear() //定义一个比例尺 3 .domain([min,max]) //设定x轴的值域 4 .range([0,width-padding.left - padding.right]) //设定x轴的定义域 5 6 var yscale = d3.scale.linear() //定义一个比例尺 7 .domain([0,gdpmax*1.1]) //设定y轴的值域 8 .range([height-padding.top-padding.bottom,0]) //设定y轴的定义域
x轴的定义域是2000~2013,此外为了代码简洁手动指定了,实际应用时应从数据中获取。y轴的定义域是0~gdpmax*1.1,乘以1.1是为了使得图形不在坐标轴的边界绘制。接下来根据数据定义一个线段生成器:
1 //创建一个线段生成器 2 var linepath = d3.svg.line() //创建一个线段生成器 3 4 .x(function(d){return xscale(d[0])}) //设置x坐标的访问器 5 .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>元素,并设置为线段生成器计算的路径。代码:
1 //定义两个颜色 2 var colors = [d3.rgb(0,0,255),d3.rgb(0,255,0)] 3 4 //添加路径 5 svg.selectall("path") //选择svg中所有的path 6 .data(datalist) //绑定数据 7 .enter() //获取enter部分 8 .append("path") //添加足够数量的<path>元素 9 .attr("transform","translate("+padding.left + "," + padding.top + ")") //平移 10 .attr("d",function(d){ 11 return linepath(d.gdp) //返回线段生成器得到的路径 12 }) 13 .attr("fill","none") //填充色为none 14 .attr("stroke",function(d,i){ 15 return colors[i] //设置折线颜色 16 }) 17 .attr("stroke-width","3px") //设置折线的宽度
添加元素的形式"selectall().data().enter().append()",相信大家都已经很熟悉了。给属性transform赋予适当的值,令折线平移到指定的位置。在<path>元素的d属性中,使用线段生成器计算路径,注意linepath()的参数是d.gdp。此处的线段生成器是按照数组gdp的格式来设定访问器的,因此一定要以d.gdp,而不是以d作为参数。
接下来绘制坐标轴:
1 //坐标轴x轴 2 var xaxis = d3.svg.axis() //创建一个新坐标轴 3 .scale(xscale) //设定x坐标轴的比例尺 4 .ticks(6) //设定x坐标轴的分隔数 5 .tickformat(d3.format("d")) //刻度的数组用字符串表示 6 .orient("bottom") //设定x坐标轴的方向 7 //坐标轴y轴 8 var yaxis = d3.svg.axis() //创建一个新坐标 9 .scale(yscale) //设定y坐标轴的比例尺 10 .orient("left") //设定y坐标轴的方向 11 12 //添加一个<g>元素用于放x轴 13 svg.append("g") //添加一个<g>元素 14 .attr("class","axis") //定义class名 15 .attr("transform","translate("+padding.left + "," + (height-padding.bottom) + ")") //平移 16 .call(xaxis) //call()应用 17 18 //添加一个<g>元素用于放y轴 19 svg.append("g") //添加一个<g>元素 20 .attr("class","axis") //定义class名 21 .attr("transform","translate("+ padding.left + "," + padding.top + ")") //平移 22 .call(yaxis) //call()应用
由于x轴的刻度是年份的意思,而数据里的数据类型确实整数类型:所以如果直接在坐标轴上显示,2000年会显示成2,000, 2002年会显示成2,002多一个逗号。因此,加了一条tickformat(),其中,d3.format("d")表示刻度的数组都用字符串表示。设定之后,年份之间的逗号就会消失。然后,将两个坐标轴分别放到两个<g>元素里。
现在的效果图如下:
如果想要使一段一段的直线看起来更光滑一些,可以使用直线生成器的插值函数。之前给大家介绍过,不清楚的点: https://www.cnblogs.com/littlespill/p/10850364.html 设置为basis模式后,线段变为曲线。
1 //创建一个线段生成器 2 var linepath = d3.svg.line() //创建一个线段生成器 3 .interpolate("basis") //使用basis插值模式 4 .x(function(d){return xscale(d[0])}) //设置x坐标的访问器 5 .y(function(d){return yscale(d[1])}) //设置y坐标的访问器
效果图:
上面的折线图还缺少一个标记,用户不知道哪条直线是中国的gdp,哪条是日本的gdp。可添加两个矩形,分别填充为相应的颜色。矩形边上添加表示国家名称的文字。
1 //添加两个矩形标记 2 var g = svg.selectall("rect") //将选择集赋值给变量g 3 .data(datalist) //绑定数据 4 .enter() //获取enter()部分 5 .append("g") //添加<g>元素 6 g.append("rect") //在<g>元素里添加<rect>矩形 7 .attr("fill",function(d,i){ //设定颜色 8 return colors[i] 9 }) 10 .attr("transform",function(d,i){ //平移 11 var x = padding.left + i*150 12 var y = height - padding.bottom + 50 13 return "translate(" +x + "," + y + ")" 14 }) 15 .attr("width",20) //设定矩形的宽度 16 .attr("height",20) //设定矩形的高度 17 18 //添加注解 19 g.append("text") //添加文字 20 .attr("class","text") //定义class名 21 .attr("x",function(d,i){ //设定文字在x方向的位置 22 return padding.left + i * 150 + 30 23 }) 24 .attr("y",function(d,i){ //设定文字在y方向的位置 25 return height - padding.bottom + 50 + 15 26 }) 27 .text(function(d){ //设定文字的内容 28 return d.coountry 29 }) 30 .attr("font-size","15px") //设定文字的大小 31 .attr("fill","black") //设定文字的颜色
如下图: 一个完整的折线图就做好了。
完整代码:
1 import react, { component } from 'react'; 2 import * as d3 from 'd3' 3 class line extends component { 4 constructor(props) { 5 super(props); 6 this.state = {} 7 } 8 9 componentdidmount(){ 10 this.onemethod() 11 } 12 13 onemethod(){ 14 15 var width = 800; //svg绘制区域的宽度 16 var height = 600; //svg绘制区域的高度 17 18 var svg = d3.select("#body") //选择id为body的div 19 .append("svg") //在div中添加<svg> 20 .attr("width",width) //设定<svg>的宽度 21 .attr("height",height) //设定<svg>的高度 22 23 //数据 24 var datalist = [ 25 { 26 coountry : "china", 27 gdp : [ 28 [2000,11920],[2001,13170],[2002,14550],[2003,16500],[2004,19440],[2005,22870], 29 [2006,27930],[2007,35040],[2008,45470],[2009,51050],[2010,59490],[2011,73140], 30 [2012,83860],[2013,103550] 31 ] 32 }, 33 { 34 coountry : "japan", 35 gdp : [ 36 [2000,47310],[2001,41590],[2002,39800],[2003,43020],[2004,46500],[2005,45710], 37 [2006,43560],[2007,43560],[2008,48490],[2009,50350],[2010,54950],[2011,59050], 38 [2012,59370],[2013,48980] 39 ] 40 } 41 ] 42 43 //外边框 44 var padding = {top : 50 , right : 50 , bottom : 100 , left : 200}; 45 46 //计算gdp的最大值 47 var gdpmax = 0; 48 for (var i = 0; i < datalist.length ; i++){ 49 var currgdp = d3.max(datalist[i].gdp,function(d){ 50 return d[1] 51 }) 52 if(currgdp > gdpmax){ 53 gdpmax = currgdp 54 } 55 } 56 57 58 //先选出年份的最小值与最大值 59 for (var i = 0; i < datalist.length ; i++){ 60 var min = d3.min(datalist[i].gdp,function(d){return d[0]}) 61 var max = d3.max(datalist[i].gdp,function(d){return d[0]}) 62 } 63 //定义比例尺,均为线性比例尺 64 var xscale = d3.scale.linear() //定义一个比例尺 65 .domain([min,max]) //设定x轴的值域 66 .range([0,width-padding.left - padding.right]) //设定x轴的定义域 67 68 var yscale = d3.scale.linear() //定义一个比例尺 69 .domain([0,gdpmax*1.1]) //设定y轴的值域 70 .range([height-padding.top-padding.bottom,0]) //设定y轴的定义域 71 //创建一个线段生成器 72 var linepath = d3.svg.line() //创建一个线段生成器 73 .interpolate("basis") //使用basis插值模式 74 .x(function(d){return xscale(d[0])}) //设置x坐标的访问器 75 .y(function(d){return yscale(d[1])}) //设置y坐标的访问器 76 77 //定义两个颜色 78 var colors = [d3.rgb(0,0,255),d3.rgb(0,255,0)] 79 80 //添加路径 81 svg.selectall("path") //选择svg中所有的path 82 .data(datalist) //绑定数据 83 .enter() //获取enter部分 84 .append("path") //添加足够数量的<path>元素 85 .attr("transform","translate("+padding.left + "," + padding.top + ")") //平移 86 .attr("d",function(d){ 87 return linepath(d.gdp) //返回线段生成器得到的路径 88 }) 89 .attr("fill","none") //填充色为none 90 .attr("stroke",function(d,i){ 91 return colors[i] //设置折线颜色 92 }) 93 .attr("stroke-width","3px") //设置折线的宽度 94 95 //坐标轴x轴 96 var xaxis = d3.svg.axis() //创建一个新坐标轴 97 .scale(xscale) //设定x坐标轴的比例尺 98 .ticks(6) //设定x坐标轴的分隔数 99 .tickformat(d3.format("d")) //刻度的数组用字符串表示 100 .orient("bottom") //设定x坐标轴的方向 101 //坐标轴y轴 102 var yaxis = d3.svg.axis() //创建一个新坐标 103 .scale(yscale) //设定y坐标轴的比例尺 104 .orient("left") //设定y坐标轴的方向 105 106 //添加一个<g>元素用于放x轴 107 svg.append("g") //添加一个<g>元素 108 .attr("class","axis") //定义class名 109 .attr("transform","translate("+padding.left + "," + (height-padding.bottom) + ")") //平移 110 .call(xaxis) //call()应用 111 112 //添加一个<g>元素用于放y轴 113 svg.append("g") //添加一个<g>元素 114 .attr("class","axis") //定义class名 115 .attr("transform","translate("+ padding.left + "," + padding.top + ")") //平移 116 .call(yaxis) //call()应用 117 118 //添加两个矩形标记 119 var g = svg.selectall("rect") //将选择集赋值给变量g 120 .data(datalist) //绑定数据 121 .enter() //获取enter()部分 122 .append("g") //添加<g>元素 123 g.append("rect") //在<g>元素里添加<rect>矩形 124 .attr("fill",function(d,i){ //设定颜色 125 return colors[i] 126 }) 127 .attr("transform",function(d,i){ //平移 128 var x = padding.left + i*150 129 var y = height - padding.bottom + 50 130 return "translate(" +x + "," + y + ")" 131 }) 132 .attr("width",20) //设定矩形的宽度 133 .attr("height",20) //设定矩形的高度 134 135 //添加注解 136 g.append("text") //添加文字 137 .attr("class","text") //定义class名 138 .attr("x",function(d,i){ //设定文字在x方向的位置 139 return padding.left + i * 150 + 30 140 }) 141 .attr("y",function(d,i){ //设定文字在y方向的位置 142 return height - padding.bottom + 50 + 15 143 }) 144 .text(function(d){ //设定文字的内容 145 return d.coountry 146 }) 147 .attr("font-size","15px") //设定文字的大小 148 .attr("fill","black") //设定文字的颜色 149 150 } 151 152 render() { 153 return ( 154 <div id="body" > 155 156 </div> 157 ); 158 } 159 } 160 161 export default line;
推荐阅读
-
D3.js(v3)+react 实现带坐标与比例尺的散点图 (V3版本)
-
D3.js(v3)+react 实现带坐标与比例尺的柱形图 (V3版本)
-
D3.js(v3)+react 制作 一个带坐标轴与比例尺的折线图
-
D3.js(v3)+react 实现带坐标与比例尺的柱形图 (V3版本)
-
D3.js(v3)+react 实现带坐标与比例尺的散点图 (V3版本)
-
D3.js(v3)+react 制作 一个带坐标与比例尺的柱形图 (V3版本)
-
D3.js(v3)+react 制作 一个带坐标轴与比例尺的折线图
-
D3.js(v3)+react 制作 一个带坐标与比例尺的散点图 (V3版本)
-
D3.js(v3)+react 制作 一个带坐标与比例尺的柱形图 (V3版本)