尝试使用Circle Tooltip创建折线图

时间:2019-05-09 03:25:36

标签: javascript css datetime d3.js svg

我正在尝试创建一个带有圆圈工具提示的交互式折线图,如以下https://bl.ocks.org/alandunning/cfb7dcd7951826b9eacd54f0647f48d3

我的代码如下: HTML

<head>
  <style>
    circle {
      fill: steelblue;
    }

    body {
      font: 12px Arial;
    }

    path {
      stroke: steelblue;
      stroke-width: 2;
      fill: none;
    }

    .axis path,
    .axis line {
      fill: none;
      stroke: grey;
      stroke-width: 1;
      shape-rendering: crispEdges;
    }

    div.tooltip {
      position: absolute;
      text-align: center;
      width: 80px;
      height: 64px;
      padding: 2px;
      font: 14px sans-serif;
      color: black;
      background: lightsteelblue;
      border: 0px;
      border-radius: 8px;
      pointer-events: none;
    }

    .overlay {
      fill: none;
      pointer-events: all;
    }

    .focus circle {
      fill: #F1F3F3;
      stroke: #6F257F;
      stroke-width: 5px;
    }

    .hover-line {
      stroke: #6F257F;
      stroke-width: 2px;
      stroke-dasharray: 3, 3;
    }
  </style>

</head>

<body>
  <svg class='line-chart2'></svg>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="./regression.js"></script>

</body>

regression.js

var gdp = [387.65, 410.32, 415.73, 452.69, 462.14,
  478.96, 508.06, 599.59, 699.68, 808.90,
  920.31, 1201.11, 1186.95, 1323.94, 1656.61,
  1823.04, 1827.63, 1856.72, 2039.12, 2102.39,
  2274.22, 2600.81
]; //y or GDP of India
var years = ['1996', '1997', '1998', '1999', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017'];

var data_gdp = []
for (i = 0; i < years.length; i++) {
  data_gdp.push({
    year: years[i],
    value: gdp[i]
  })
}

function drawChart_gdp(data, class_name) {
  var svgWidth = 1200,
    svgHeight = 400;
  var margin = {
    top: 60,
    right: 60,
    bottom: 60,
    left: 60
  };
  var width = svgWidth - margin.left - margin.right;
  var height = svgHeight - margin.top - margin.bottom;
  var svg = d3.select(class_name)
    .attr("width", svgWidth)
    .attr("height", svgHeight);
  var bisectDate = d3.bisector(function(d) {
    return d.year;
  }).left;
  var g = svg.append("g")
    .attr("transform",
      "translate(" + margin.left + "," + margin.top + ")"
    );
  var x = d3.scaleTime().range([0, width]);
  var y = d3.scaleLinear().rangeRound([height, 0]);

  var line = d3.line()
    .x(function(d) {
      return x(new Date(parseInt(d.year), 0))
    })
    .y(function(d) {
      return y(d.value)
    })
  x.domain(d3.extent(data, function(d) {
    return new Date(parseInt(d.year), 0);
  }));
  y.domain(d3.extent(data, function(d) {
    return d.value
  }));

  g.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x))
    .append("text")
    .attr("fill", "#000")
    .text("Year")
    .attr("dy", "1.90em")
    .attr("y", 5)
    .attr("x", 500)
    .attr("font-size", "20px")
    .select(".domain")
    .remove();

  g.append("g")
    .call(d3.axisLeft(y))
    .append("text")
    .attr("fill", "#000")
    .attr("transform", "rotate(-90)")
    .attr("y", -80)
    .attr("x", -55)
    .attr("dy", "1.90em")
    .attr("text-anchor", "center")
    .attr("font-size", "20px")
    .text("GDP ($)")

  g.append("path")
    .datum(data)
    .attr("fill", "none")
    .attr("stroke", "steelblue")
    .attr("stroke-linejoin", "round")
    .attr("stroke-linecap", "round")
    .attr("stroke-width", 1.5)
    .attr("d", line);

  var focus = g.append("g")
    .attr("class", "focus")
    .style("display", "none");

  focus.append("line")
    .attr("class", "x-hover-line hover-line")
    .attr("y1", 0)
    .attr("y2", height);

  focus.append("line")
    .attr("class", "y-hover-line hover-line")
    .attr("x1", width)
    .attr("x2", width);

  focus.append("circle")
    .attr("r", 7.5);

  focus.append("text")
    .attr("x", 15)
    .attr("dy", ".31em");

  svg.append("rect")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height)
    .on("mouseover", function() {
      focus.style("display", null);
    })
    .on("mouseout", function() {
      focus.style("display", "none");
    })
    .on("mousemove", function() { //problem in this function
      var x0 = d3.timeFormat("%Y")(x.invert(d3.mouse(this)[0])),
        i = bisectDate(data, x0, 1);
      d0 = data[i - 1],
        d1 = data[i],
        d = x0 - d0.year > d1.year - x0 ? d1 : d0;
      focus.attr("transform", "translate(" + x(d.year) + "," + y(d.value) + ")");
      focus.select("text").text(function() {
        return d.value;
      });
      focus.select(".x-hover-line").attr("y2", height - y(d.value));
      focus.select(".y-hover-line").attr("x2", width + width);
    });

}
drawChart_gdp(data_gdp, '.line-chart2');

我怀疑在示例中,数据是从json文件中拉出的,但这里是从问题所在的数组中拉出的,而且我认为数据的类型也有点不同。我的目标只是创建一个显示y轴值的圆形工具。

1 个答案:

答案 0 :(得分:0)

您现在面临的问题与previous question中面临的问题几乎相反:在该问题中,问题在于您正在处理日期对象,就像它们是字符串一样。

现在,您正在处理字符串,就好像它们是日期对象一样。在您的数据中,year只是一个字符串,例如"1996"

所以,这个:

focus.attr("transform", "translate(" + x(d.year) + "," + y(d.value) + ")");

应该是:

focus.attr("transform", "translate(" + x(d3.timeParse("%Y")(d.year)) + "," + y(d.value) + ")"); 
//parsing to a date here----------------------^

以下是具有此更改的代码:

var gdp = [387.65, 410.32, 415.73, 452.69, 462.14,
  478.96, 508.06, 599.59, 699.68, 808.90,
  920.31, 1201.11, 1186.95, 1323.94, 1656.61,
  1823.04, 1827.63, 1856.72, 2039.12, 2102.39,
  2274.22, 2600.81
]; //y or GDP of India
var years = ['1996', '1997', '1998', '1999', '2000', '2001', '2002', '2003', '2004', '2005', '2006', '2007', '2008', '2009', '2010', '2011', '2012', '2013', '2014', '2015', '2016', '2017'];

var data_gdp = []
for (i = 0; i < years.length; i++) {
  data_gdp.push({
    year: years[i],
    value: gdp[i]
  })
}

function drawChart_gdp(data, class_name) {
  var svgWidth = 1200,
    svgHeight = 400;
  var margin = {
    top: 60,
    right: 60,
    bottom: 60,
    left: 60
  };
  var width = svgWidth - margin.left - margin.right;
  var height = svgHeight - margin.top - margin.bottom;
  var svg = d3.select(class_name)
    .attr("width", svgWidth)
    .attr("height", svgHeight);
  var bisectDate = d3.bisector(function(d) {
    return d.year;
  }).left;
  var g = svg.append("g")
    .attr("transform",
      "translate(" + margin.left + "," + margin.top + ")"
    );
  var x = d3.scaleTime().range([0, width]);
  var y = d3.scaleLinear().rangeRound([height, 0]);

  var line = d3.line()
    .x(function(d) {
      return x(new Date(parseInt(d.year), 0))
    })
    .y(function(d) {
      return y(d.value)
    })
  x.domain(d3.extent(data, function(d) {
    return new Date(parseInt(d.year), 0);
  }));
  y.domain(d3.extent(data, function(d) {
    return d.value
  }));

  g.append("g")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x))
    .append("text")
    .attr("fill", "#000")
    .text("Year")
    .attr("dy", "1.90em")
    .attr("y", 5)
    .attr("x", 500)
    .attr("font-size", "20px")
    .select(".domain")
    .remove();

  g.append("g")
    .call(d3.axisLeft(y))
    .append("text")
    .attr("fill", "#000")
    .attr("transform", "rotate(-90)")
    .attr("y", -80)
    .attr("x", -55)
    .attr("dy", "1.90em")
    .attr("text-anchor", "center")
    .attr("font-size", "20px")
    .text("GDP ($)")

  g.append("path")
    .datum(data)
    .attr("fill", "none")
    .attr("stroke", "steelblue")
    .attr("stroke-linejoin", "round")
    .attr("stroke-linecap", "round")
    .attr("stroke-width", 1.5)
    .attr("d", line);

  var focus = g.append("g")
    .attr("class", "focus")
    .style("display", "none");

  focus.append("line")
    .attr("class", "x-hover-line hover-line")
    .attr("y1", 0)
    .attr("y2", height);

  focus.append("line")
    .attr("class", "y-hover-line hover-line")
    .attr("x1", width)
    .attr("x2", width);

  focus.append("circle")
    .attr("r", 7.5);

  focus.append("text")
    .attr("x", 15)
    .attr("dy", ".31em");

  svg.append("rect")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height)
    .on("mouseover", function() {
      focus.style("display", null);
    })
    .on("mouseout", function() {
      focus.style("display", "none");
    })
    .on("mousemove", function() { //problem in this function
      var x0 = d3.timeFormat("%Y")(x.invert(d3.mouse(this)[0])),
        i = bisectDate(data, x0, 1);
      d0 = data[i - 1],
        d1 = data[i],
        d = x0 - d0.year > d1.year - x0 ? d1 : d0;
      focus.attr("transform", "translate(" + x(d3.timeParse("%Y")(d.year)) + "," + y(d.value) + ")");
      focus.select("text").text(function() {
        return d.value;
      });
      focus.select(".x-hover-line").attr("y2", height - y(d.value));
      focus.select(".y-hover-line").attr("x2", width + width);
    });

}
drawChart_gdp(data_gdp, '.line-chart2');
<head>
  <style>
    circle {
      fill: steelblue;
    }

    body {
      font: 12px Arial;
    }

    path {
      stroke: steelblue;
      stroke-width: 2;
      fill: none;
    }

    .axis path,
    .axis line {
      fill: none;
      stroke: grey;
      stroke-width: 1;
      shape-rendering: crispEdges;
    }

    div.tooltip {
      position: absolute;
      text-align: center;
      width: 80px;
      height: 64px;
      padding: 2px;
      font: 14px sans-serif;
      color: black;
      background: lightsteelblue;
      border: 0px;
      border-radius: 8px;
      pointer-events: none;
    }

    .overlay {
      fill: none;
      pointer-events: all;
    }

    .focus circle {
      fill: #F1F3F3;
      stroke: #6F257F;
      stroke-width: 5px;
    }

    .hover-line {
      stroke: #6F257F;
      stroke-width: 2px;
      stroke-dasharray: 3, 3;
    }

  </style>

</head>

<body>
  <svg class='line-chart2'></svg>
  <script src="https://d3js.org/d3.v4.min.js"></script>
</body>

到现在为止,所有这些混乱都来自这样一个事实,即数据数组中有字符串,但是x是时间标度。我的建议:解析数据数组中的字符串,创建日期对象,并在代码中保持一致,将所有数据作为日期对象而不是字符串处理。