• 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>元素里。
     
    现在的效果图如下:
     
     
    如果想要使一段一段的直线看起来更光滑一些,可以使用直线生成器的插值函数。之前给大家介绍过,不清楚的点: 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;
     
     
     
  • 相关阅读:
    watchers
    Observer
    计算属性
    Jsonp
    跨域
    axios
    duilib入门简明教程 -- 部分bug (11) (转)
    duilib入门简明教程 -- 界面设计器 DuiDesigner (10) (转)
    duilib入门简明教程 -- 界面布局(9) (转)
    duilib入门简明教程 -- 完整的自绘标题栏(8) (转)
  • 原文地址:https://www.cnblogs.com/littleSpill/p/10891161.html
Copyright © 2020-2023  润新知