D3.js(v3)+react 製做 一個帶座標軸與比例尺的折線圖

本章使用路徑生成器來繪製一個折線圖。以中國和日本的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>元素裏。
 
如今的效果圖以下:
 
 
若是想要使一段一段的直線看起來更光滑一些,可使用直線生成器的插值函數。以前給你們介紹過,不清楚的點: http://www.javashuo.com/article/p-nrcfeleb-gu.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;
相關文章
相關標籤/搜索