【问题标题】:Unable to show the tooltip values for stacked area chart in a single tooltip无法在单个工具提示中显示堆叠面积图的工具提示值
【发布时间】:2016-07-14 05:23:39
【问题描述】:

<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
  <style>
    body {
      font: 10px sans-serif;
    }
    .axis path,
    .axis line {
      fill: none;
      stroke: #000;
      shape-rendering: crispEdges;
    }
    .x.axis path {
      display: none;
    }
    .line {
      fill: none;
      stroke: steelblue;
      stroke-width: 1.5px;
    }
  </style>
</head>

<body>
  <script>
    var myData = "date	New York	San Francisco	Austin\n\
20111001	63.4	62.7	72.2\n\
20111002	58.0	59.9	67.7\n\
20111003	53.3	59.1	69.4\n\
20111004	55.7	58.8	68.0\n\
20111005	64.2	58.7	72.4\n\
20111006	58.8	57.0	77.0\n\
20111007	57.9	56.7	82.3\n\
20111008	61.8	56.8	78.9\n\
20111009	69.3	56.7	68.8\n\
20111010	71.2	60.1	68.7\n\
20111011	68.7	61.1	70.3\n\
20111012	61.8	61.5	75.3\n\
20111013	63.0	64.3	76.6\n\
20111014	66.9	67.1	66.6\n\
20111015	61.7	64.6	68.0\n\
20111016	61.8	61.6	70.6\n\
20111017	62.8	61.1	71.1\n\
20111018	60.8	59.2	70.0\n\
20111019	62.1	58.9	61.6\n\
20111020	65.1	57.2	57.4\n\
20111021	55.6	56.4	64.3\n\
20111022	54.4	60.7	72.4\n";

    var margin = {
        top: 20,
        right: 80,
        bottom: 30,
        left: 50
      },
      width = 500 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    var parseDate = d3.time.format("%Y%m%d").parse;

    var x = d3.time.scale()
      .range([0, width]);

    var y = d3.scale.linear()
      .range([height, 0]);

    var color = d3.scale.category20();

    var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom");

    var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left");

    var line = d3.svg.line()
      .interpolate("basis")
      .x(function(d) {
        return x(d.date);
      })
      .y(function(d) {
        return y(d.temperature);
      });

    var svg = d3.select("body").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 data = d3.tsv.parse(myData);

    color.domain(d3.keys(data[0]).filter(function(key) {
      return key !== "date";
    }));

    data.forEach(function(d) {
      d.date = parseDate(d.date);
    });

    var cities = color.domain().map(function(name) {
      return {
        name: name,
        values: data.map(function(d) {
          return {
            date: d.date,
            temperature: +d[name]
          };
        })
      };
    });

    x.domain(d3.extent(data, function(d) {
      return d.date;
    }));

    y.domain([
      d3.min(cities, function(c) {
        return d3.min(c.values, function(v) {
          return v.temperature;
        });
      }),
      d3.max(cities, function(c) {
        return d3.max(c.values, function(v) {
          return v.temperature;
        });
      })
    ]);

    var legend = svg.selectAll('g')
      .data(cities)
      .enter()
      .append('g')
      .attr('class', 'legend');

    legend.append('rect')
      .attr('x', width - 20)
      .attr('y', function(d, i) {
        return i * 20;
      })
      .attr('width', 10)
      .attr('height', 10)
      .style('fill', function(d) {
        return color(d.name);
      });

    legend.append('text')
      .attr('x', width - 8)
      .attr('y', function(d, i) {
        return (i * 20) + 9;
      })
      .text(function(d) {
        return d.name;
      });

    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
      .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Temperature (ºF)");

    var city = svg.selectAll(".city")
      .data(cities)
      .enter().append("g")
      .attr("class", "city");

    city.append("path")
      .attr("class", "line")
      .attr("d", function(d) {
        return line(d.values);
      })
      .style("stroke", function(d) {
        return color(d.name);
      });

    city.append("text")
      .datum(function(d) {
        return {
          name: d.name,
          value: d.values[d.values.length - 1]
        };
      })
      .attr("transform", function(d) {
        return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")";
      })
      .attr("x", 3)
      .attr("dy", ".35em")
      .text(function(d) {
        return d.name;
      });

    var mouseG = svg.append("g")
      .attr("class", "mouse-over-effects");

    mouseG.append("path") // this is the black vertical line to follow mouse
      .attr("class", "mouse-line")
      .style("stroke", "black")
      .style("stroke-width", "1px")
      .style("opacity", "0");

    var lines = document.getElementsByClassName('line');

    var mousePerLine = mouseG.selectAll('.mouse-per-line')
      .data(cities)
      .enter()
      .append("g")
      .attr("class", "mouse-per-line");

    mousePerLine.append("rect")
      .attr("width", width / 2)
      .attr("height", 30)
      .style("padding", "5px")
      .style("stroke", function(d) {
        return color(d.name);
      })
      .style("fill", function(d) {
        return color(d.name);
      })
      .style("stroke-width", "1px")
      .style("opacity", "0")
      .attr('x', 10);


    mousePerLine.append("circle")
      .attr("r", 5)
      .style("stroke", function(d) {
        return color(d.name);
      })
      .style("fill", function(d) {
        return color(d.name);
      })
      .style("stroke-width", "1px")
      .style("opacity", "0");

    mousePerLine.append("text")
      .attr("transform", "translate(15,13)");

    mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas
      .attr('width', width) // can't catch mouse events on a g element
      .attr('height', height)
      .attr('fill', 'none')
      .attr('pointer-events', 'all')
      .on('mouseout', function() { // on mouse out hide line, circles and text
        d3.select(".mouse-line")
          .style("opacity", "0");
        d3.selectAll(".mouse-per-line rect")
          .style("opacity", "0");
        d3.selectAll(".mouse-per-line circle")
          .style("opacity", "0");
        d3.selectAll(".mouse-per-line text")
          .style("opacity", "0");
      })
      .on('mouseover', function() { // on mouse in show line, circles and text
        d3.select(".mouse-line")
          .style("opacity", "1");
        d3.selectAll(".mouse-per-line rect")
          .style("opacity", "0.5");
        d3.selectAll(".mouse-per-line circle")
          .style("opacity", "1");
        d3.selectAll(".mouse-per-line text")
          .style("opacity", "1");
      })
      .on('mousemove', function() { // mouse moving over canvas
        var mouse = d3.mouse(this);
        d3.select(".mouse-line")
          .attr("d", function() {
            var d = "M" + mouse[0] + "," + height;
            d += " " + mouse[0] + "," + 0;
            return d;
          });

        d3.selectAll(".mouse-per-line")
          .attr("transform", function(d, i) {
            console.log(width / mouse[0])
            var xDate = x.invert(mouse[0]),
              bisect = d3.bisector(function(d) {
                return d.date;
              }).right;
            idx = bisect(d.values, xDate);

            var beginning = 0,
              end = lines[i].getTotalLength(),
              target = null;

            while (true) {
              target = Math.floor((beginning + end) / 2);
              pos = lines[i].getPointAtLength(target);
              if ((target === end || target === beginning) && pos.x !== mouse[0]) {
                break;
              }
              if (pos.x > mouse[0]) end = target;
              else if (pos.x < mouse[0]) beginning = target;
              else break; //position found
            }

            d3.select(this).select('text')
              .text(y.invert(pos.y).toFixed(2));

            return "translate(" + mouse[0] + "," + pos.y + ")";
          });
      });
  </script>
</body>

</html>

你好。我是 D3 的新手。我正在尝试使用 D3 制作堆积面积图,在鼠标悬停时,我想在每个面积图的单个矩形内显​​示所有多个数据值。到目前为止,我已经实现了在每个数据值的单独矩形中显示鼠标悬停时的数据值。请帮助我提供一些关于如何实现这一目标的建议/提示。我真的陷入困境和挣扎。提前感谢您的帮助。

【问题讨论】:

  • 你到底想要什么?如果是单个工具提示,圆圈和位置呢?
  • 感谢您的快速回复。我希望,当鼠标悬停时,线条应该在各自的位置显示圆圈以突出显示数据点,并且线条旁边应该有一个矩形来显示数据,而不是现在在我的情况下显示的 3 个矩形.在鼠标悬停时,当圆圈突出显示数据点时,相应的数据值将显示在单个矩形中,而不是单个矩形中。我看到了一个链接nvd3.org/livecode/index.html#codemirrorNav,但它使用的是 NVD3。但我的要求是只使用 D3。是否可以??请帮帮我。
  • 请帮我提出一些建议,比如可以使用 D3 还是不使用 D3?
  • 如果你能告诉我文字和圆圈是如何更新的,我可能会提供帮助
  • 获取单个矩形很容易,而绑定数据将城市作为单个元素,例如 [cities] 而不是城市

标签: javascript d3.js stacked-area-chart


【解决方案1】:

好吧,这真是太丑了。我知道如何解决这个问题(我也是 d3 的新手)。

我将解释我是如何解决的。应该并且将会有更好的方法来实现您的目标。

  1. 首先我添加了一个组而不是三个组
  2. 接下来为每个 svg 元素(即矩形、文本和圆形)分别添加数据。
  3. 在鼠标悬停时,我选择了 mouse-per-line 类元素,然后分别转换了每个 svg 元素。
  4. 为了附加矩形,我将高度存储在一个数组中并取平均值来设置矩形的位置。同样对文本进行了相同的转换。

再一次,我的只是一个工作模型,将会/应该有更优雅的方式来实现同样的目标。

如果有人可以修改或建议编辑,那将很有帮助。 希望这可以帮助。

    var myData = "date	New York	San Francisco	Austin\n\
20111001	63.4	62.7	72.2\n\
20111002	58.0	59.9	67.7\n\
20111003	53.3	59.1	69.4\n\
20111004	55.7	58.8	68.0\n\
20111005	64.2	58.7	72.4\n\
20111006	58.8	57.0	77.0\n\
20111007	57.9	56.7	82.3\n\
20111008	61.8	56.8	78.9\n\
20111009	69.3	56.7	68.8\n\
20111010	71.2	60.1	68.7\n\
20111011	68.7	61.1	70.3\n\
20111012	61.8	61.5	75.3\n\
20111013	63.0	64.3	76.6\n\
20111014	66.9	67.1	66.6\n\
20111015	61.7	64.6	68.0\n\
20111016	61.8	61.6	70.6\n\
20111017	62.8	61.1	71.1\n\
20111018	60.8	59.2	70.0\n\
20111019	62.1	58.9	61.6\n\
20111020	65.1	57.2	57.4\n\
20111021	55.6	56.4	64.3\n\
20111022	54.4	60.7	72.4\n";

    var margin = {
        top: 20,
        right: 80,
        bottom: 30,
        left: 50
      },
      width = 500 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    var parseDate = d3.time.format("%Y%m%d").parse;

    var x = d3.time.scale()
      .range([0, width]);

    var y = d3.scale.linear()
      .range([height, 0]);

    var color = d3.scale.category20();

    var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom");

    var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left");

    var line = d3.svg.line()
      .interpolate("basis")
      .x(function(d) {
        return x(d.date);
      })
      .y(function(d) {
        return y(d.temperature);
      });

    var svg = d3.select("body").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 data = d3.tsv.parse(myData);

    color.domain(d3.keys(data[0]).filter(function(key) {
      return key !== "date";
    }));

    data.forEach(function(d) {
      d.date = parseDate(d.date);
    });

    var cities = color.domain().map(function(name) {
      return {
        name: name,
        values: data.map(function(d) {
          return {
            date: d.date,
            temperature: +d[name]
          };
        })
      };
    });

    x.domain(d3.extent(data, function(d) {
      return d.date;
    }));

    y.domain([
      d3.min(cities, function(c) {
        return d3.min(c.values, function(v) {
          return v.temperature;
        });
      }),
      d3.max(cities, function(c) {
        return d3.max(c.values, function(v) {
          return v.temperature;
        });
      })
    ]);

    var legend = svg.selectAll('g')
      .data(cities)
      .enter()
      .append('g')
      .attr('class', 'legend');

    legend.append('rect')
      .attr('x', width - 20)
      .attr('y', function(d, i) {
        return i * 20;
      })
      .attr('width', 10)
      .attr('height', 10)
      .style('fill', function(d) {
        return color(d.name);
      });

    legend.append('text')
      .attr('x', width - 8)
      .attr('y', function(d, i) {
        return (i * 20) + 9;
      })
      .text(function(d) {
        return d.name;
      });

    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
      .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Temperature (ºF)");

    var city = svg.selectAll(".city")
      .data(cities)
      .enter().append("g")
      .attr("class", "city");

    city.append("path")
      .attr("class", "line")
      .attr("d", function(d) {
        return line(d.values);
      })
      .style("stroke", function(d) {
        return color(d.name);
      });

    city.append("text")
      .datum(function(d) {
        return {
          name: d.name,
          value: d.values[d.values.length - 1]
        };
      })
      .attr("transform", function(d) {
        return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")";
      })
      .attr("x", 3)
      .attr("dy", ".35em")
      .text(function(d) {
        return d.name;
      });

    var mouseG = svg.append("g")
      .attr("class", "mouse-over-effects");

    mouseG.append("path") // this is the black vertical line to follow mouse
      .attr("class", "mouse-line")
      .style("stroke", "black")
      .style("stroke-width", "1px")
      .style("opacity", "0");

    var lines = document.getElementsByClassName('line');

    var mousePerLine = mouseG.selectAll('.mouse-per-line')
      .data([cities])
      .enter()
      .append("g")
      .attr("class", "mouse-per-line");

    mousePerLine.selectAll('.mouse-per-line')
      .data([cities])
      .enter().append("rect")
      .attr("width", width / 2)
      .attr("height", 90)
      .style("padding", "5px")
      .style("stroke", function(d) {
        return color(d.name);
      })
      .style("fill", function(d) {
        return color(d.name);
      })
      .style("stroke-width", "1px")
      .style("opacity", "0")
      .attr('x', 10)
	  .attr('y',-45);


    mousePerLine.selectAll('.mouse-per-line')
      .data(cities)
      .enter().append("circle")
      .attr("r", 5)
      .style("stroke", function(d) {
        return color(d.name);
      })
      .style("fill", function(d) {
        return color(d.name);
      })
      .style("stroke-width", "1px")
      .style("opacity", "0");

    mousePerLine.selectAll('.text')
      .data(cities)
      .enter().append("text")
      .attr("transform", "translate(15,13)");

    mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas
      .attr('width', width) // can't catch mouse events on a g element
      .attr('height', height)
      .attr('fill', 'none')
      .attr('pointer-events', 'all')
      .on('mouseout', function() { // on mouse out hide line, circles and text
        d3.select(".mouse-line")
          .style("opacity", "0");
        d3.selectAll(".mouse-per-line rect")
          .style("opacity", "0");
        d3.selectAll(".mouse-per-line circle")
          .style("opacity", "0");
        d3.selectAll(".mouse-per-line text")
          .style("opacity", "0");
      })
      .on('mouseover', function() { // on mouse in show line, circles and text
        d3.select(".mouse-line")
          .style("opacity", "1");
        d3.selectAll(".mouse-per-line rect")
          .style("opacity", "0.5");
        d3.selectAll(".mouse-per-line circle")
          .style("opacity", "1");
        d3.selectAll(".mouse-per-line text")
          .style("opacity", "1");
      })
      .on('mousemove', function() { // mouse moving over canvas
        var mouse = d3.mouse(this);
        d3.select(".mouse-line")
          .attr("d", function() {
            var d = "M" + mouse[0] + "," + height;
            d += " " + mouse[0] + "," + 0;
            return d;
          });
		d3.selectAll(".mouse-per-line")
          .attr("foo", function(d, i) {
            var xDate = x.invert(mouse[0]);
            var  bisect;
			var  heights = [];
			
			d3.selectAll('circle')
				.attr("transform", function(d,j) {
					bisect = d3.bisector(function(d) {
                return d.date;
              }).right;
            idx = bisect(d.values, xDate);

            var beginning = 0,
              end = lines[i].getTotalLength(),
              target = null;

            while (true) {
              target = Math.floor((beginning + end) / 2);
              pos = lines[j].getPointAtLength(target);
              if ((target === end || target === beginning) && pos.x !== mouse[0]) {
                break;
              }
              if (pos.x > mouse[0]) end = target;
              else if (pos.x < mouse[0]) beginning = target;
              else break; //position found
            }
				heights[j] = pos.y;
				return "translate(" + mouse[0] + "," + pos.y + ")";
				});
			var avgheight = 0;	
			for(var z = 0; z < heights.length ; z++){
				avgheight = avgheight + heights[z];
			}	
			avgheight = avgheight/d.length;
						d3.select(this).selectAll('rect')
				.attr("transform", function(d,i) {
					return "translate(" + mouse[0] + "," + avgheight + ")";
				});
			var rectangleText = "";
			for(var t = 1; t < heights.length ; t++) {
				rectangleText = rectangleText + "<br/>" + y.invert(heights[t]).toFixed(2);
			}	
            d3.select(this).selectAll('text').text(function(d,i) { return y.invert(heights[i]).toFixed(2)}).attr("transform", function(d,i) {
					return "translate(" + mouse[0] + "," + (avgheight  + 30 - (i*25)) + ")";
				}).attr("dx", '20px');			
            return "translate(" + mouse[0] + "," + pos.y + ")";
          });
      });
body {
      font: 10px sans-serif;
    }
    .axis path,
    .axis line {
      fill: none;
      stroke: #000;
      shape-rendering: crispEdges;
    }
    .x.axis path {
      display: none;
    }
    .line {
      fill: none;
      stroke: steelblue;
      stroke-width: 1.5px;
    }
<!DOCTYPE html>
<html>

<head>
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>

<body>
  
</body>

</html>

【讨论】:

  • 您好,非常感谢您的回复。我看了你的解释,真的很棒。你是对的,与这个相比,早期的外观和感觉更好,因为这可能有些令人困惑。好吧,感谢您提供如此出色的解决方案和解释。 :)
【解决方案2】:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
  <script data-require="d3@3.5.3" data-semver="3.5.3" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
  <style>
    body {
      font: 10px sans-serif;
    }
    .axis path,
    .axis line {
      fill: none;
      stroke: #000;
      shape-rendering: crispEdges;
    }
    .x.axis path {
      display: none;
    }
    .line {
      fill: none;
      stroke: steelblue;
      stroke-width: 1.5px;
    }
  </style>
</head>

<body>
  <script>
    var myData = "date	New York	San Francisco	Austin\n\
20111001	63.4	62.7	72.2\n\
20111002	58.0	59.9	67.7\n\
20111003	53.3	59.1	69.4\n\
20111004	55.7	58.8	68.0\n\
20111005	64.2	58.7	72.4\n\
20111006	58.8	57.0	77.0\n\
20111007	57.9	56.7	82.3\n\
20111008	61.8	56.8	78.9\n\
20111009	69.3	56.7	68.8\n\
20111010	71.2	60.1	68.7\n\
20111011	68.7	61.1	70.3\n\
20111012	61.8	61.5	75.3\n\
20111013	63.0	64.3	76.6\n\
20111014	66.9	67.1	66.6\n\
20111015	61.7	64.6	68.0\n\
20111016	61.8	61.6	70.6\n\
20111017	62.8	61.1	71.1\n\
20111018	60.8	59.2	70.0\n\
20111019	62.1	58.9	61.6\n\
20111020	65.1	57.2	57.4\n\
20111021	55.6	56.4	64.3\n\
20111022	54.4	60.7	72.4\n";

    var margin = {
        top: 20,
        right: 80,
        bottom: 30,
        left: 50
      },
      width = 500 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    var parseDate = d3.time.format("%Y%m%d").parse;

    var x = d3.time.scale()
      .range([0, width]);

    var y = d3.scale.linear()
      .range([height, 0]);

    var color = d3.scale.category20();

    var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom");

    var yAxis = d3.svg.axis()
      .scale(y)
      .orient("left");

    var line = d3.svg.line()
      .interpolate("basis")
      .x(function(d) {
        return x(d.date);
      })
      .y(function(d) {
        return y(d.temperature);
      });

    var svg = d3.select("body").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 data = d3.tsv.parse(myData);

    color.domain(d3.keys(data[0]).filter(function(key) {
      return key !== "date";
    }));

    data.forEach(function(d) {
      d.date = parseDate(d.date);
    });

    var cities = color.domain().map(function(name) {
      return {
        name: name,
        values: data.map(function(d) {
          return {
            date: d.date,
            temperature: +d[name]
          };
        })
      };
    });

    x.domain(d3.extent(data, function(d) {
      return d.date;
    }));

    y.domain([
      d3.min(cities, function(c) {
        return d3.min(c.values, function(v) {
          return v.temperature;
        });
      }),
      d3.max(cities, function(c) {
        return d3.max(c.values, function(v) {
          return v.temperature;
        });
      })
    ]);

    var legend = svg.selectAll('g')
      .data(cities)
      .enter()
      .append('g')
      .attr('class', 'legend');

    legend.append('rect')
      .attr('x', width - 20)
      .attr('y', function(d, i) {
        return i * 20;
      })
      .attr('width', 10)
      .attr('height', 10)
      .style('fill', function(d) {
        return color(d.name);
      });

    legend.append('text')
      .attr('x', width - 8)
      .attr('y', function(d, i) {
        return (i * 20) + 9;
      })
      .text(function(d) {
        return d.name;
      });

    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
      .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Temperature (ºF)");

    var city = svg.selectAll(".city")
      .data(cities)
      .enter().append("g")
      .attr("class", "city");

    city.append("path")
      .attr("class", "line")
      .attr("d", function(d) {
        return line(d.values);
      })
      .style("stroke", function(d) {
        return color(d.name);
      });

    city.append("text")
      .datum(function(d) {
        return {
          name: d.name,
          value: d.values[d.values.length - 1]
        };
      })
      .attr("transform", function(d) {
        return "translate(" + x(d.value.date) + "," + y(d.value.temperature) + ")";
      })
      .attr("x", 3)
      .attr("dy", ".35em")
      .text(function(d) {
        return d.name;
      });

     // **************************************************************************************** //

    var mouseG = svg.append("g")
      .attr("class", "mouse-over-effects");

    mouseG.append("path") // this is the black vertical line to follow mouse
      .attr("class", "mouse-line")
      .style("stroke", "black")
      .style("stroke-width", "1px")
      .style("opacity", "0");

    var lines = document.getElementsByClassName('line');

    var mousePerLine = mouseG.selectAll('.mouse-per-line')
      .data([cities])
      .enter()
      .append("g")
      .attr("class", "mouse-per-line");

    mousePerLine.selectAll('.mouse-per-line') // Rectangle
      .data([cities])
      .enter()
      .append("rect")
      .attr("width", width)
      .attr("height", 110)
      .style("padding", "5px")
      .style("stroke", "#272525")
      .style("fill", "#272525")
      .style("stroke-width", "1px")
      .style("opacity", "0")
      .attr('x', 10)
      .attr('y', -45);

    mousePerLine.selectAll('.mouse-per-line') // Circle
      .data(cities)
      .enter()
      .append("circle")
      .attr("r", 5)
      .style("stroke", function(d) {
        return color(d.name);
      })
      .style("fill", function(d) {
        return color(d.name);
      })
      .style("stroke-width", "1px")
      .style("opacity", "0");

    mousePerLine.append("text")
      .attr("class", "DateText");
    mousePerLine.selectAll('.mouse-per-line') // Text
      .data(cities)
      .enter()
      .append("text")
      .attr("class", "ValueText")
      .attr("transform", "translate(15,13)")
      .style("fill", function(d) {
        return color(d.name);
      })
      .style("font-weight", "bold")
      .style("font-size", "10pt");

    mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas
      .attr('width', width) // can't catch mouse events on a g element
      .attr('height', height)
      .attr('fill', 'none')
      .attr('pointer-events', 'all')
      .on('mouseout', function() { // on mouse out hide line, circles and text
        d3.select(".mouse-line")
          .style("opacity", "0");
        d3.selectAll(".mouse-per-line rect")
          .style("opacity", "0");
        d3.selectAll(".mouse-per-line circle")
          .style("opacity", "0");
        d3.selectAll(".mouse-per-line text")
          .style("opacity", "0");
      })
      .on('mouseover', function() { // on mouse in show line, circles and text
        d3.select(".mouse-line")
          .style("opacity", "1");
        d3.selectAll(".mouse-per-line rect")
          .style("opacity", "1");
        d3.selectAll(".mouse-per-line circle")
          .style("opacity", "1");
        d3.selectAll(".mouse-per-line text")
          .style("opacity", "1");
      })
      // **************************************************************************************** //

    .on('mousemove', function() { // mouse moving over canvas
      var mouse = d3.mouse(this);
      d3.select(".mouse-line")
        .attr("d", function() {
          var d = "M" + mouse[0] + "," + height;
          d += " " + mouse[0] + "," + 0;
          return d;
        });

      d3.selectAll(".mouse-per-line")
        .attr("foo", function(d, i) {
          var xDate = x.invert(mouse[0]);
          var bisect;
          var heights = [];

          var xDateValue = /\w*.\s.\d.\d*.\d*.:\d*.:\d*/.exec(xDate);
          // console.log(xDateValue);

          d3.selectAll('circle')
            .attr("transform", function(d, j) {
              bisect = d3.bisector(function(d) {
                return d.date;
              }).right;
              idx = bisect(d.values, xDate);

              var beginning = 0,
                end = lines[i].getTotalLength(),
                target = null;

              while (true) {
                target = Math.floor((beginning + end) / 2);
                pos = lines[j].getPointAtLength(target);
                if ((target === end || target === beginning) && pos.x !== mouse[0]) {
                  break;
                }
                if (pos.x > mouse[0]) end = target;
                else if (pos.x < mouse[0]) beginning = target;
                else break; //position found
              }
              heights[j] = pos.y;
              return "translate(" + mouse[0] + "," + pos.y + ")";
            });
          var avgheight = 0;
          for (var z = 0; z < heights.length; z++) {
            avgheight = avgheight + heights[z];
          }
          avgheight = avgheight / d.length;

          d3.select(this).selectAll('rect')
            .attr("transform", function(d, i) {
              return "translate(" + mouse[0] + "," + avgheight + ")";
            });
          var rectangleText = "";
          for (var t = 1; t < heights.length; t++) {
            rectangleText = rectangleText + "<br/>" + y.invert(heights[t]).toFixed(2);
          }

          d3.select(this)
            .select(".DateText")
            .text(xDateValue)
            .attr("transform", function(d, i) {
              return "translate(" + mouse[0] + "," + (avgheight - 25) + ")";
            })
            .attr("dx", '20px')
            .style("fill", "white")
            .style("font-weight", "bold")
            .style("font-size", "10pt");;

          d3.select(this)
            .selectAll('.ValueText')
            .text(function(d, i) {
              return d.name + "  " + y.invert(heights[i]).toFixed(2)
            })
            .attr("transform", function(d, i) {
              return "translate(" + mouse[0] + "," + (avgheight + 50 - (i * 25)) + ")";
            })
            .attr("dx", '20px');

          return "translate(" + mouse[0] + "," + pos.y + ")";
        });
    });
  </script>
</body>

</html>

经过这么多障碍,我找到了一些解决方案,可以使用 D3 库在多线图中显示 X 轴和 Y 轴的数据。我希望它对任何尝试相同的人有所帮助。

【讨论】:

    猜你喜欢
    • 2022-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-18
    • 1970-01-01
    相关资源
    最近更新 更多