D3:在工具提示中显示折线图时出现问题

时间:2019-02-20 21:02:46

标签: javascript d3.js

我有一个条形图,如果将鼠标悬停在其上,我想在工具提示中看到一个折线图。 到目前为止,我的实现方式。

<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8' />
  <title>Simple Bar chart</title>
  <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.min.js"></script>
  <style>
    body {
      font-family: "Arial", sans-serif;
    }
    
    .bar {
      fill: gray;
      /*#5f89ad;*/
    }
    
    rect:hover {
      fill: #1E90FF;
    }
    
    .axis {
      font-size: 13px;
    }
    
    .axis path,
    .axis line {
      fill: none;
      display: none;
    }
    
    .label {
      font-size: 13px;
      font-weight: bold;
    }
    
    text {
      font-weight: bold;
    }
    /*.d3-tip {
  line-height: 1;
  padding: 6px;
  background: wheat;
  border-radius: 4px solid black;
  font-size: 12px;
}*/
  </style>

</head>

<body>

  <div id="graphic"></div>

  <script>
    var data = [{
        country: 'Bangladesh',
        population_2012: 105905297,
        growth: {
          year_2013: 42488,
          year_2014: 934,
          year_2015: 52633,
          year_2016: 112822,
          year_2017: 160792
        }
      },
      {
        country: 'Ethopia',
        population_2012: 75656319,
        growth: {
          year_2013: 1606010,
          year_2014: 1606705,
          year_2015: 1600666,
          year_2016: 1590077,
          year_2017: 1580805
        }
      },
      {
        country: 'Kenya',
        population_2012: 33007327,
        growth: {
          year_2013: 705153,
          year_2014: 703994,
          year_2015: 699906,
          year_2016: 694295,
          year_2017: 687910
        }
      },
      {
        country: 'Afghanistan',
        population_2012: 23280573,
        growth: {
          year_2013: 717151,
          year_2014: 706082,
          year_2015: 665025,
          year_2016: 616262,
          year_2017: 573643
        }
      },
      {
        country: 'Morocco',
        population_2012: 13619520,
        growth: {
          year_2013: 11862,
          year_2014: 7997,
          year_2015: 391,
          year_2016: -8820,
          year_2017: -17029
        }
      }
    ];

    data_growth = [];

    for (i = 0; i < data.length; i++) {
      data[i]["total_population"] = data[i]["population_2012"] + data[i].growth["year_2013"] + data[i].growth["year_2014"] + data[i].growth["year_2015"] + data[i].growth["year_2016"] + data[i].growth["year_2017"];

      data[i]["growth_data"] = [{
          "year": 2013,
          "growth": (data[i].growth["year_2013"] / (data[i]["population_2012"])) * 100
        },
        {
          "year": 2014,
          "growth": (data[i].growth["year_2014"] / (data[i]["population_2012"] + data[i].growth["year_2013"])) * 100
        },
        {
          "year": 2015,
          "growth": (data[i].growth["year_2015"] / (data[i]["population_2012"] + data[i].growth["year_2013"] + data[i].growth["year_2014"])) * 100
        },
        {
          "year": 2016,
          "growth": (data[i].growth["year_2016"] / (data[i]["population_2012"] + data[i].growth["year_2013"] + data[i].growth["year_2014"] + data[i].growth["year_2015"])) * 100
        },
        {
          "year": 2017,
          "growth": (data[i].growth["year_2017"] / (data[i]["population_2012"] + data[i].growth["year_2013"] + data[i].growth["year_2014"] + data[i].growth["year_2015"] + data[i].growth["year_2016"])) * 100
        }
      ];
    }

    //console.log(data);

    //sort bars based on population_2012
    data = data.sort(function(a, b) {
      return d3.ascending(a.population_2012, b.population_2012);
    })

    //set up svg using margin conventions - we'll need plenty of room on the left for labels
    var margin = {
      top: 15,
      right: 25,
      bottom: 15,
      left: 160
    };

    var width = 960 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    var svg = d3.select("#graphic").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var tool_tip = d3.tip()
      .attr("class", "d3-tip")
      .offset([20, 670])
      .html("<p>This is a SVG inside a tooltip:</p><div id='tipDiv'></div>");

    svg.call(tool_tip);

    var x = d3.scale.linear()
      .range([0, width])
      .domain([0, d3.max(data, function(d) {
        return d.population_2012;
      })]);

    var y = d3.scale.ordinal()
      .rangeRoundBands([height, 0], .1)
      .domain(data.map(function(d) {
        return d.country;
      }));

    //make y axis to show bar countrys
    var yAxis = d3.svg.axis()
      .scale(y)
      //no tick marks
      .tickSize(0)
      .orient("left");

    var gy = svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)

    var bars = svg.selectAll(".bar")
      .data(data)
      .enter()
      .append("g")

    //append rects
    bars.append("rect")
      .attr("class", "bar")
      .attr("y", function(d) {
        return y(d.country);
      })
      .attr("height", y.rangeBand())
      .attr("x", 0)
      .attr("width", function(d) {
        return x(d.total_population);
      })
      .attr('fill', 'gray')
      .on('mouseover', function(d) {
        /////////draw(d["growth_data"]);
        //console.log("d:==>", d);
        tool_tip.show();
        var tipSVG = d3.select("#tipDiv")
          .append("svg")
          .attr("width", 300) //200
          .attr("height", 200); //50

        // set the ranges
        //var x = d3.scalePoint().range([0, width]);
        var x = d3.scale.ordinal().range([0, width]);
        //var y = d3.scaleLinear().range([height, 0]);
        var y = d3.scale.ordinal().range([height, 0]);


        // text label for the x axis
        tipSVG.append("text")
          .attr("transform",
            "translate(" + (width / 2) + " ," +
            (height + margin.top + 20) + ")")
          .style("text-anchor", "middle")
          .text("Date");

        // define the line
        var valueline = d3.svg.line()
          .x(function(d) {
            return x(d.key);
          })
          .y(function(d) {
            return y(d.value);
          });
        ////////////END

        d["growth_data"].forEach(function(d) {
          d.value = +d.value;
        });

        /*data.sort(function(a, b) {
				return a.value - b.value;
			  })*/

        // Scale the range of the data
        x.domain(d["growth_data"].map(function(d) {
          return d.key;
        }));
        y.domain([0, d3.max(d["growth_data"], function(d) {
          return d.value;
        })]);

        // Add the valueline path.
        tipSVG.append("path")
          .datum(d["growth_data"])
          .attr("class", "line")
          .attr("d", valueline);

        tipSVG.append("g")
          .attr("transform", "translate(0," + height + ")")
          .call(d3.axisBottom(x));

        // Add the Y Axis
        tipSVG.append("g")
          .call(d3.axisLeft(y));



      })
      .on('mouseout', tool_tip.hide);

    //add a population_2012 label to the right of each bar
    bars.append("text")
      .attr("class", "label")
      //y position of the label is halfway down the bar
      .attr("y", function(d) {
        return y(d.country) + y.rangeBand() / 2 + 4;
      })
      //x position is 3 pixels to the right of the bar
      .attr("x", 15
        /*(function (d) {
                        return x(d.population_2012) + 3;
                    }*/
      )
      .text(function(d) {
        return d.total_population.toLocaleString();
      });
  </script>

</body>

</html>

由于我是D3的新手,因此我试图在.on('mouseover', )期间显示折线图,但不够清晰。

使用此代码,我遇到错误,不确定该怎么做:

  

“消息”:“未捕获的TypeError:d3.axisBottom不是函数”,

当我尝试在tipSVG中显示简单形状时,它工作正常吗? 有人可以建议我犯什么错误吗?

1 个答案:

答案 0 :(得分:0)

我已经解决了问题。由于来自d3.v3和d3.v4的API的混合出现了问题。我将整个代码反向移植到d3.v3实现。 在此处发布此代码,以便如果有人希望在另一个图表的工具提示中看到图表的实现。

这是完整的代码:

<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8' />
  <title>Question 5 </title>
  <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.min.js"></script>
  <style>
    body {
      font-family: "Arial", sans-serif;
    }
    
    .bar {
      fill: gray;
      /*#5f89ad;*/
    }
    
    rect:hover {
      fill: #1E90FF;
    }
    
    .axis {
      font-size: 13px;
    }
    
    .axis path,
    .axis line {
      fill: none;
      /*display: none;*/
    }
    
    .label {
      font-size: 13px;
      font-weight: bold;
    }
    
    text {
      font-weight: bold;
    }
    
    .line {
      fill: none;
      stroke: steelblue;
      stroke-width: 2px;
    }
    
    .axis path,
    .axis line {
      fill: none;
      stroke: #000;
      shape-rendering: crispEdges;
    }
    /*.d3-tip {
  line-height: 1;
  padding: 6px;
  background: wheat;
  border-radius: 4px solid black;
  font-size: 12px;
}*/
  </style>

</head>

<body>

  <div id="graphic"></div>

  <script>
    var data = [{
        country: 'Bangladesh',
        population_2012: 105905297,
        growth: {
          year_2013: 42488,
          year_2014: 934,
          year_2015: 52633,
          year_2016: 112822,
          year_2017: 160792
        }
      },
      {
        country: 'Ethopia',
        population_2012: 75656319,
        growth: {
          year_2013: 1606010,
          year_2014: 1606705,
          year_2015: 1600666,
          year_2016: 1590077,
          year_2017: 1580805
        }
      },
      {
        country: 'Kenya',
        population_2012: 33007327,
        growth: {
          year_2013: 705153,
          year_2014: 703994,
          year_2015: 699906,
          year_2016: 694295,
          year_2017: 687910
        }
      },
      {
        country: 'Afghanistan',
        population_2012: 23280573,
        growth: {
          year_2013: 717151,
          year_2014: 706082,
          year_2015: 665025,
          year_2016: 616262,
          year_2017: 573643
        }
      },
      {
        country: 'Morocco',
        population_2012: 13619520,
        growth: {
          year_2013: 11862,
          year_2014: 7997,
          year_2015: 391,
          year_2016: -8820,
          year_2017: -17029
        }
      }
    ];

    data_growth = [];

    for (i = 0; i < data.length; i++) {
      data[i]["total_population"] = data[i]["population_2012"] + data[i].growth["year_2013"] + data[i].growth["year_2014"] + data[i].growth["year_2015"] + data[i].growth["year_2016"] + data[i].growth["year_2017"];

      data[i]["growth_data"] = [{
          "year": 2013,
          "growth": (data[i].growth["year_2013"] / (data[i]["population_2012"])) * 100
        },
        {
          "year": 2014,
          "growth": (data[i].growth["year_2014"] / (data[i]["population_2012"] + data[i].growth["year_2013"])) * 100
        },
        {
          "year": 2015,
          "growth": (data[i].growth["year_2015"] / (data[i]["population_2012"] + data[i].growth["year_2013"] + data[i].growth["year_2014"])) * 100
        },
        {
          "year": 2016,
          "growth": (data[i].growth["year_2016"] / (data[i]["population_2012"] + data[i].growth["year_2013"] + data[i].growth["year_2014"] + data[i].growth["year_2015"])) * 100
        },
        {
          "year": 2017,
          "growth": (data[i].growth["year_2017"] / (data[i]["population_2012"] + data[i].growth["year_2013"] + data[i].growth["year_2014"] + data[i].growth["year_2015"] + data[i].growth["year_2016"])) * 100
        }
      ];
    }

    //console.log(data);

    //sort bars based on population_2012
    data = data.sort(function(a, b) {
      return d3.ascending(a.population_2012, b.population_2012);
    })

    //set up svg using margin conventions - we'll need plenty of room on the left for labels
    var margin = {
      top: 15,
      right: 25,
      bottom: 15,
      left: 160
    };

    var width = 960 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    var svg = d3.select("#graphic").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var tool_tip = d3.tip()
      .attr("class", "d3-tip")
      .offset([20, 670])
      .html("<p>Year-wise growth in population for the country:</p><div id='tipDiv' style='border: 1px solid gray;'></div>");

    svg.call(tool_tip);

    var x = d3.scale.linear()
      .range([0, width])
      .domain([0, d3.max(data, function(d) {
        return d.population_2012;
      })]);

    var y = d3.scale.ordinal()
      .rangeRoundBands([height, 0], .1)
      .domain(data.map(function(d) {
        return d.country;
      }));

    //make y axis to show bar countrys
    var yAxis = d3.svg.axis()
      .scale(y)
      //no tick marks
      .tickSize(0)
      .orient("left");

    var gy = svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)

    var bars = svg.selectAll(".bar")
      .data(data)
      .enter()
      .append("g")

    //append rects
    bars.append("rect")
      .attr("class", "bar")
      .attr("y", function(d) {
        return y(d.country);
      })
      .attr("height", y.rangeBand())
      .attr("x", 0)
      .attr("width", function(d) {
        return x(d.total_population);
      })
      .attr('fill', 'gray')
      .on('mouseover', function(d) {

        //console.log("d:==>", d);
        tool_tip.show();
        var points_raw = d3.range(10).map(function() {
          return {
            year: Math.random() * 100,
            growth: Math.random() * 100
          }
        });
        //console.log("points-raw:==>", points_raw)
        // sort by x, descending
        //points = points_raw.sort(function(a, b) { return d3.descending(a.year, b.year); });
        //points = points_raw
        points = d["growth_data"];

        // set up canvas
        var margin = {
            top: 20,
            right: 30,
            bottom: 40,
            left: 60
          },
          width = 420 - margin.left - margin.right,
          height = 320 - margin.top - margin.bottom;

        var x = d3.scale.linear()
          .range([0, width])
          .domain(points.map(function(d) {
            return d.year;
          }));;

        var y = d3.scale.linear()
          .range([height, 0])
          .domain([0, d3.max(points, function(d) {
            return d.growth;
          })]);

        var x_axis = d3.svg.axis()
          .scale(x)
          .ticks(5)
          .orient('bottom');

        var y_axis = d3.svg.axis()
          .scale(y)
          .orient('left');

        // define line function
        var line = d3.svg.line()
          .x(function(d) {
            return x(d.year);
          })
          .y(function(d) {
            return y(d.growth);
          })
        /*.interpolate('basis')*/
        ;

        /*points.forEach(function(d) {
    d.growth = +d.growth;
  });*/

        // add svg element
        var svg = d3.select('#tipDiv')
          .append('svg')
          .attr("width", width + margin.left + margin.right)
          .attr("height", height + margin.top + margin.bottom)
          .append("g")
          .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        // determine domain ranges for x/y
        x.domain(d3.extent(points, function(d) {
          return d.year;
        }));
        /*x.domain(points.map(function (d) {
                return d["year"];
            }));
	*/
        y.domain(d3.extent(points, function(d) {
          return d.growth;
        }));

        // add labeled x-axis
        svg.append('g')
          .attr('class', 'x axis')
          .attr("transform", "translate(0," + height + ")")
          .call(x_axis)
          .append("text")
          .attr("x", width - 6)
          .attr("dy", "3em")
          /*.style("text-anchor", "end")*/
          .text("Year");

        // add labeled y-axis
        svg.append('g')
          .attr('class', 'y axis')
          .call(y_axis)
          .append("text")
          .attr("transform", "rotate(-90)")
          .attr("y", 0)
          .attr("dy", "-3.71em")
          .style("text-anchor", "end")
          .text("Pct %");

        // draw line
        svg.append('path')
          .datum(points)
          .attr('class', 'line')
          .attr('d', line);

      })
      .on('mouseout', tool_tip.hide);

    //add a population_2012 label to the right of each bar
    bars.append("text")
      .attr("class", "label")
      //y position of the label is halfway down the bar
      .attr("y", function(d) {
        return y(d.country) + y.rangeBand() / 2 + 4;
      })
      //x position is 3 pixels to the right of the bar
      .attr("x", 15
        /*(function (d) {
                        return x(d.population_2012) + 3;
                    }*/
      )
      .text(function(d) {
        return d.total_population.toLocaleString();
      });
  </script>

</body>

</html>