本章使用路徑生成器來繪製一個折線圖。以中國和日本的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;