欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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>元素里。
 
现在的效果图如下:
 
D3.js(v3)+react 制作 一个带坐标轴与比例尺的折线图
 
如果想要使一段一段的直线看起来更光滑一些,可以使用直线生成器的插值函数。之前给大家介绍过,不清楚的点: 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坐标的访问器

 

效果图:
 
D3.js(v3)+react 制作 一个带坐标轴与比例尺的折线图
 
上面的折线图还缺少一个标记,用户不知道哪条直线是中国的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")               //设定文字的颜色

 

如下图: 一个完整的折线图就做好了。
 
 D3.js(v3)+react 制作 一个带坐标轴与比例尺的折线图
 
 
 
 完整代码: 
 
  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;