将工具提示功能添加到d3.js多系列折线图

时间:2016-09-21 12:54:58

标签: javascript html css d3.js

我最初可能没有很好地解释自己要完成的事情。我想将https://jsfiddle.net/qvf8hn0u/的工具提示功能(代码如下)合并到我的多系列折线图中。 (见下面的代码)。在将鼠标悬停在图表上时在浏览器中查看工具提示代码时,会出现2个工具提示,其中x和y值与图表上的数据相关联。我的目标是根据所选类别的数量在多系列折线图中完成相同的功能。在多系列图表中,可以从图表右侧的类别列表中进行选择。应用工具提示功能后,它将显示x和y数据的值。如果我选择一个类别,它只会显示所选类别的数据,如果我选择2个或更多,它将显示所选类别的数据。我希望这可以解决任何最初的困惑。我们将非常感谢任何协助。

工具提示:

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Tooltip</title>
<style>

text.inner-circle {
  font-weight: 400;
  font-size: 12px;
  text-transform: uppercase;
}

text.inner-text {
  font-weight: 400;
  font-size: 36px;
  font-family: 'Metric Regular', 'Metric';
  text-align: center;
  font-style: normal;
  text-transform: uppercase;
}

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

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

.grid .tick {
  stroke: lightgrey;
  stroke-opacity: 0.7;
  shape-rendering: crispEdges;
}

.grid path {
  stroke-width: 0;
}


</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>

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

var z = d3.scale.category20c();

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 parseDate = d3.time.format("%Y-%m-%dT%H:%M:%S.%LZ");

var data = [{
  data: [
    ["2016-01-20T05:31:17.000Z", 95.9, {}],
    ["2016-01-20T05:31:47.000Z", 95.9, {}],
    ["2016-01-20T05:32:17.000Z", 95.4, {}],
    ["2016-01-20T05:32:47.000Z", 96.1, {}],
    ["2016-01-20T05:33:17.000Z", 95.7, {}],
    ["2016-01-20T05:33:47.000Z", 95.9, {}],
    ["2016-01-20T05:34:17.000Z", 95.5, {}],
    ["2016-01-20T05:34:47.000Z", 95.9, {}],
    ["2016-01-20T05:35:17.000Z", 95.8, {}],
    ["2016-01-20T05:35:47.000Z", 95.9, {}],
    ["2016-01-20T05:36:17.000Z", 95.7, {}],
    ["2016-01-20T05:36:47.000Z", 95.7, {}],
    ["2016-01-20T05:37:17.000Z", 95.9, {}],
    ["2016-01-20T05:37:47.000Z", 95.5, {}],
    ["2016-01-20T05:38:17.000Z", 95.4, {}],
    ["2016-01-20T05:38:47.000Z", 95.8, {}],
    ["2016-01-20T05:39:17.000Z", 96.0, {}],
    ["2016-01-20T05:39:47.000Z", 96.1, {}],
    ["2016-01-20T05:40:17.000Z", 95.8, {}],
    ["2016-01-20T05:40:47.000Z", 96.0, {}],
    ["2016-01-20T05:41:17.000Z", 95.9, {}],
    ["2016-01-20T05:41:47.000Z", 94.9, {}],
    ["2016-01-20T05:42:17.000Z", 95.8, {}],
    ["2016-01-20T05:42:47.000Z", 95.9, {}],
    ["2016-01-20T05:43:17.000Z", 95.8, {}],
    ["2016-01-20T05:43:47.000Z", 96.0, {}],
    ["2016-01-20T05:44:17.000Z", 95.7, {}],
    ["2016-01-20T05:44:47.000Z", 96.0, {}],
    ["2016-01-20T05:45:17.000Z", 95.9, {}],
    ["2016-01-20T05:45:47.000Z", 96.0, {}],
    ["2016-01-20T05:46:17.000Z", 95.8, {}],
    ["2016-01-20T05:46:47.000Z", 96.0, {}],
    ["2016-01-20T05:47:17.000Z", 95.7, {}],
    ["2016-01-20T05:47:47.000Z", 96.2, {}],
    ["2016-01-20T05:48:17.000Z", 95.8, {}],
    ["2016-01-20T05:48:47.000Z", 95.9, {}],
    ["2016-01-20T05:49:17.000Z", 95.7, {}],
    ["2016-01-20T05:49:47.000Z", 95.9, {}],
    ["2016-01-20T05:50:18.000Z", 95.7, {}],
    ["2016-01-20T05:50:48.000Z", 95.8, {}],
    ["2016-01-20T05:51:18.000Z", 95.7, {}],
    ["2016-01-20T05:51:48.000Z", 95.9, {}],
    ["2016-01-20T05:52:18.000Z", 95.5, {}],
    ["2016-01-20T05:52:48.000Z", 95.9, {}],
    ["2016-01-20T05:53:18.000Z", 95.8, {}],
    ["2016-01-20T05:53:48.000Z", 95.9, {}],
    ["2016-01-20T05:54:18.000Z", 95.7, {}],
    ["2016-01-20T05:54:48.000Z", 95.9, {}],
    ["2016-01-20T05:55:18.000Z", 95.8, {}],
    ["2016-01-20T05:55:48.000Z", 95.8, {}],
    ["2016-01-20T05:56:18.000Z", 95.6, {}],
    ["2016-01-20T05:56:48.000Z", 95.7, {}],
    ["2016-01-20T05:57:18.000Z", 95.7, {}],
    ["2016-01-20T05:57:48.000Z", 95.8, {}],
    ["2016-01-20T05:58:18.000Z", 95.7, {}],
    ["2016-01-20T05:58:48.000Z", 95.7, {}],
    ["2016-01-20T05:59:18.000Z", 95.6, {}],
    ["2016-01-20T05:59:48.000Z", 95.8, {}],
    ["2016-01-20T06:00:18.000Z", 95.7, {}],
    ["2016-01-20T06:00:48.000Z", 95.7, {}],
    ["2016-01-20T06:01:18.000Z", 95.6, {}],
    ["2016-01-20T06:01:48.000Z", 95.7, {}],
    ["2016-01-20T06:02:18.000Z", 95.8, {}],
    ["2016-01-20T06:02:48.000Z", 95.8, {}],
    ["2016-01-20T06:03:18.000Z", 95.8, {}],
    ["2016-01-20T06:03:48.000Z", 95.8, {}],
    ["2016-01-20T06:04:18.000Z", 95.8, {}],
    ["2016-01-20T06:04:48.000Z", 95.8, {}],
    ["2016-01-20T06:05:18.000Z", 95.7, {}],
    ["2016-01-20T06:05:48.000Z", 95.7, {}]
  ],
  label: "a"
}, {
  data: [
    ["2016-01-20T05:31:17.000Z", 90.9, {}],
    ["2016-01-20T05:31:47.000Z", 91.9, {}],
    ["2016-01-20T05:32:17.000Z", 92.4, {}],
    ["2016-01-20T05:32:47.000Z", 90.1, {}],
    ["2016-01-20T05:33:17.000Z", 89.7, {}],
    ["2016-01-20T05:33:47.000Z", 91.9, {}],
    ["2016-01-20T05:34:17.000Z", 85.5, {}],
    ["2016-01-20T05:34:47.000Z", 93.9, {}],
    ["2016-01-20T05:35:17.000Z", 94.8, {}],
    ["2016-01-20T05:35:47.000Z", 93.9, {}],
    ["2016-01-20T05:36:17.000Z", 92.7, {}],
    ["2016-01-20T05:36:47.000Z", 95.7, {}],
    ["2016-01-20T05:37:17.000Z", 92.9, {}],
    ["2016-01-20T05:37:47.000Z", 93.5, {}],
    ["2016-01-20T05:38:17.000Z", 93.4, {}],
    ["2016-01-20T05:38:47.000Z", 93.8, {}],
    ["2016-01-20T05:39:17.000Z", 93.0, {}],
    ["2016-01-20T05:39:47.000Z", 93.1, {}],
    ["2016-01-20T05:40:17.000Z", 93.8, {}],
    ["2016-01-20T05:40:47.000Z", 93.0, {}],
    ["2016-01-20T05:41:17.000Z", 93.9, {}],
    ["2016-01-20T05:41:47.000Z", 93.9, {}],
    ["2016-01-20T05:42:17.000Z", 92.8, {}],
    ["2016-01-20T05:42:47.000Z", 92.9, {}],
    ["2016-01-20T05:43:17.000Z", 93.8, {}],
    ["2016-01-20T05:43:47.000Z", 93.0, {}],
    ["2016-01-20T05:44:17.000Z", 93.7, {}],
    ["2016-01-20T05:44:47.000Z", 93.0, {}],
    ["2016-01-20T05:45:17.000Z", 93.9, {}],
    ["2016-01-20T05:45:47.000Z", 93.0, {}],
    ["2016-01-20T05:46:17.000Z", 93.8, {}],
    ["2016-01-20T05:46:47.000Z", 96.0, {}],
    ["2016-01-20T05:47:17.000Z", 92.7, {}],
    ["2016-01-20T05:47:47.000Z", 92.2, {}],
    ["2016-01-20T05:48:17.000Z", 92.8, {}],
    ["2016-01-20T05:48:47.000Z", 92.9, {}],
    ["2016-01-20T05:49:17.000Z", 92.7, {}],
    ["2016-01-20T05:49:47.000Z", 92.9, {}],
    ["2016-01-20T05:50:18.000Z", 93.7, {}],
    ["2016-01-20T05:50:48.000Z", 93.8, {}],
    ["2016-01-20T05:51:18.000Z", 92.7, {}],
    ["2016-01-20T05:51:48.000Z", 92.9, {}],
    ["2016-01-20T05:52:18.000Z", 92.5, {}],
    ["2016-01-20T05:52:48.000Z", 94.9, {}],
    ["2016-01-20T05:53:18.000Z", 94.8, {}],
    ["2016-01-20T05:53:48.000Z", 94.9, {}],
    ["2016-01-20T05:54:18.000Z", 94.7, {}],
    ["2016-01-20T05:54:48.000Z", 94.9, {}],
    ["2016-01-20T05:55:18.000Z", 94.8, {}],
    ["2016-01-20T05:55:48.000Z", 93.8, {}],
    ["2016-01-20T05:56:18.000Z", 94.6, {}],
    ["2016-01-20T05:56:48.000Z", 94.7, {}],
    ["2016-01-20T05:57:18.000Z", 93.7, {}],
    ["2016-01-20T05:57:48.000Z", 93.8, {}],
    ["2016-01-20T05:58:18.000Z", 93.7, {}],
    ["2016-01-20T05:58:48.000Z", 93.7, {}],
    ["2016-01-20T05:59:18.000Z", 93.6, {}],
    ["2016-01-20T05:59:48.000Z", 93.8, {}],
    ["2016-01-20T06:00:18.000Z", 93.7, {}],
    ["2016-01-20T06:00:48.000Z", 93.7, {}],
    ["2016-01-20T06:01:18.000Z", 93.6, {}],
    ["2016-01-20T06:01:48.000Z", 94.7, {}],
    ["2016-01-20T06:02:18.000Z", 94.8, {}],
    ["2016-01-20T06:02:48.000Z", 94.8, {}],
    ["2016-01-20T06:03:18.000Z", 94.8, {}],
    ["2016-01-20T06:03:48.000Z", 94.8, {}],
    ["2016-01-20T06:04:18.000Z", 94.8, {}],
    ["2016-01-20T06:04:48.000Z", 94.8, {}],
    ["2016-01-20T06:05:18.000Z", 94.7, {}],
    ["2016-01-20T06:05:48.000Z", 94.7, {}]
  ],
  label: "b"
}]


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

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

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

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

var line = d3.svg.line()
  .interpolate("monotone")
  .x(function(d) {
    return x(parseDate.parse(d[0]));
  })
  .y(function(d) {
    return y(d[1]);
  });

var ary = [];
data.forEach(function(d) {
  ary.push(d.data);
});

x.domain(d3.extent(d3.merge(ary), function(d) {
  return parseDate.parse(d[0]);
}));

y.domain([
  d3.min(data, function(c) {
    return d3.min(c.data, function(v) {
      return v[1];
    });
  }),
  d3.max(data, function(c) {
    return d3.max(c.data, function(v) {
      return v[1];
    });
  })
]);

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

svg.append("g")
  .attr("class", "y axis")
  .call(yAxis);

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

series.append("path")
  .attr("class", "line")
  .attr("d", function(d) {
    return line(d.data);
  })
  .style("stroke", function(d, i) {
    return z(i);
  });

series.append("text")
  .datum(function(d) {
    return {
      label: d.label,
      data: d.data[d.data.length - 1]
    };
  })
  .attr("transform", function(d) {
    return "translate(" + x(parseDate.parse(d.data[0])) + "," + y(d.data[1]) + ")";
  })
  .attr("x", 3)
  .attr("dy", ".35em");

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(data)
  .enter()
  .append("g")
  .attr("class", "mouse-per-line");

mousePerLine.append("circle")
  .attr("r", 7)
  .style("stroke", function(d, i) {
    return z(i);
  })
  .style("fill", "none")
  .style("stroke-width", "1px")
  .style("opacity", "0");

mousePerLine.append("rect")
    .attr("x", 0)
    .attr("y", -10)
  .attr("width", 85)
    .attr("height", 35)
  .style("stroke", function(d, i) {
    return z(i);
  })
    .attr("class", "tooltip-container")
  .style("fill", "red")
    .style("opacity", "0")
  .style("stroke-width", "1px");


mousePerLine.append("text").attr("id", "x-text")
  .attr("transform", "translate(10,3)");
mousePerLine.append("text").attr("id", "y-text")
  .attr("transform", "translate(10,23)");


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 circle")
      .style("opacity", "0");
      d3.selectAll(".mouse-per-line rect")
      .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 circle")
      .style("opacity", "1");
    d3.selectAll(".mouse-per-line text")
      .style("opacity", "1");
        d3.selectAll(".mouse-per-line rect")
      .style("opacity", "0.2");             
  })
  .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[0];
          }).right;
        var idx = bisect(d.data, xDate);

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

        while (true) {
          target = Math.floor((beginning + end) / 2);
          var 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('#y-text')
          .text("y: " + y.invert(pos.y).toFixed(2));

                d3.select(this).select('#x-text')
          .text("x: " + d3.time.format("%X")(xDate));

        return "translate(" + mouse[0] + "," + pos.y + ")";
      });
  });

</script>
</html>

多系列折线图:

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Issues Ratings</title>
<style>

body {
  font: 10px sans-serif;
}

.axis path,
.axis line, 
.axis1 path,
.axis1 line {
  fill: none;
  stroke: #E6E7E8;
  shape-rendering: crispEdges;
}

.x.axis path, .x.axis1 path {
  display: none;
}

.line {
  fill: none;
  stroke-width: 1.5px;
}

.legend-box {
  cursor: pointer;  
}

#mouse-tracker {
  stroke: #E6E7E8;
  stroke-width: 1px;
}

.hover-line { 
  stroke: #E6E7E8;
  fill: none;
  stroke-width: 1px;
  left: 10px;
  shape-rendering: crispEdges;
  opacity: 1e-6;
}

.hover-text {
  stroke: none;
  font-size: 30px;
  font-weight: bold;
  fill: #000000;
}

.tooltip {
  font-weight: normal;
}

.brush .extent {
  stroke: #FFF;
  shape-rendering: crispEdges;
}

  text.inner-circle {
  font-weight: 400;
  font-size: 12px;
  text-transform: uppercase;
}

  text.inner-text {
  font-weight: 400;
  font-size: 36px;
  font-family: 'Metric Regular', 'Metric';
  text-align: center;
  font-style: normal;
  text-transform: uppercase;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>

var margin = {top: 20, right: 200, bottom: 100, left: 50},
    margin2 = { top: 430, right: 10, bottom: 20, left: 40 },
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom,
    height2 = 500 - margin2.top - margin2.bottom;

var parseDate = d3.time.format("%Y%m%d").parse;
var bisectDate = d3.bisector(function(d) { return d.date; }).left;

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

    xScale2 = d3.time.scale()
    .range([0, width]); // Duplicate xScale for brushing ref later

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

// 40 Custom DDV colors 
var color = d3.scale.ordinal().range(["#48A36D",  "#56AE7C",  "#64B98C", "#72C39B", "#80CEAA", "#80CCB3",
"#7FC9BD", "#7FC7C6", "#7EC4CF", "#7FBBCF", "#7FB1CF", "#80A8CE", "#809ECE", "#8897CE", "#8F90CD",
"#9788CD", "#9E81CC", "#AA81C5", "#B681BE", "#C280B7", "#CE80B0", "#D3779F", "#D76D8F", "#DC647E",
"#E05A6D", "#E16167", "#E26962", "#E2705C", "#E37756", "#E38457", "#E39158", "#E29D58", "#E2AA59",
"#E0B15B", "#DFB95C", "#DDC05E", "#DBC75F", "#E3CF6D", "#EAD67C", "#F2DE8A"]);  


var xAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom")
    .ticks(10),

    xAxis2 = d3.svg.axis() // xAxis for brush slider
    .scale(xScale2)
    .orient("bottom")
    .ticks(10);    

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

var yAxis = d3.svg.axis()
    .scale(xScale)
    .orient("bottom")
    .ticks(15)
    .tickSize(-height, 0, 0)
    .tickFormat("");

var yAxis = d3.svg.axis()
    .scale(yScale)
    .orient("left")
    .ticks(15)
    .tickSize(-width, 0, 0)
    .tickFormat("");


var line = d3.svg.line()
    .interpolate("basis")
    .x(function(d) { return xScale(d.date); })
    .y(function(d) { return yScale(d.rating); })
    //.defined(function(d) { return d.rating; });  // Hiding line value defaults of 0 for missing data

var maxY; // Defined later to update yAxis

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

// Create invisible rect for mouse tracking
svg.append("rect")
    .attr("width", width)
    .attr("height", height)                                    
    .attr("x", 0) 
    .attr("y", 0)
    //.attr("id", "mouse-tracker")
    .style("fill", "white"); 

//for slider part-----------------------------------------------------------------------------------

//end slider part----------------------------------------------------------------------------------- 

d3.tsv("http://bl.ocks.org/DStruths/raw/9c042e3a6b66048b5bd4/data.tsv", function(error, data) { 
  color.domain(d3.keys(data[0]).filter(function(key) { // Set the domain of the color ordinal scale to be all the csv headers except "date", matching a color to an issue
    return key !== "date"; 
  }));

  data.forEach(function(d) { // Make every date in the csv data a javascript date object format
    d.date = parseDate(d.date);
  });

  var categories = color.domain().map(function(name) { // Nest the data into an array of objects with new keys

    return {
      name: name, // "name": the csv headers except date
      values: data.map(function(d) { // "values": which has an array of the dates and ratings
        return {
          date:d.date, 
          rating:+(d[name]),
          };
      }),
      visible: (name === "Unemployment" ? true : false) // "visible": all false except for economy which is true.
    };
  });

  xScale.domain(d3.extent(data, function(d) { return d.date; })); // extent = highest and lowest points, domain is data, range is bouding box

  yScale.domain([0, 70
    //d3.max(categories, function(c) { return d3.max(c.values, function(v) { return v.rating; }); })
  ]);


 //for slider part-----------------------------------------------------------------------------------

  //end slider part-----------------------------------------------------------------------------------

  // draw line graph
  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("x", -10)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Issues Rating");

 svg.append("g")
      .attr("class", "grid")
      .attr("transform", "translate(0," + height + ")")
      .call(xGrid);

  svg.append("g")
      .attr("class", "grid")
      .call(yGrid)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("x", -10)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Issues Rating");


  var issue = svg.selectAll(".issue")
      .data(categories) // Select nested data and append to new svg group elements
    .enter().append("g")
      .attr("class", "issue");   

  issue.append("path")
      .attr("class", "line")
      .style("pointer-events", "none") // Stop line interferring with cursor
      .attr("id", function(d) {
        return "line-" + d.name.replace(" ", "").replace("/", ""); // Give line id of line-(insert issue name, with any spaces replaced with no spaces)
      })
      .attr("d", function(d) { 
        return d.visible ? line(d.values) : null; // If array key "visible" = true then draw line, if not then don't 
      })
      .attr("clip-path", "url(#clip)")//use clip path to make irrelevant part invisible
      .style("stroke", function(d) { return color(d.name); });

  // draw legend
  var legendSpace = 650 / categories.length; // 450/number of issues (ex. 40)    

  issue.append("rect")
      .attr("width", 10)
      .attr("height", 10)                                    
      .attr("x", width + (margin.right/3) - 15) 
      .attr("y", function (d, i) { return (legendSpace)+i*(legendSpace) - 8; })  // spacing
      .attr("fill",function(d) {
        return d.visible ? color(d.name) : "#F1F1F2"; // If array key "visible" = true then color rect, if not then make it grey 
      })
      .attr("class", "legend-box")

      .on("click", function(d){ // On click make d.visible 
        d.visible = !d.visible; // If array key for this data selection is "visible" = true then make it false, if false then make it true

        maxY = findMaxY(categories); // Find max Y rating value categories data with "visible"; true
        yScale.domain([0,maxY]); // Redefine yAxis domain based on highest y value of categories data with "visible"; true
        svg.select(".y.axis")
          .transition()
          .call(yAxis);   

        issue.select("path")
          .transition()
          .attr("d", function(d){
            return d.visible ? line(d.values) : null; // If d.visible is true then draw line for this d selection
          })

        issue.select("rect")
          .transition()
          .attr("fill", function(d) {
          return d.visible ? color(d.name) : "#F1F1F2";
        });
      })

  issue.append("text")
      .attr("x", width + (margin.right/3)) 
      .attr("y", function (d, i) { return (legendSpace)+i*(legendSpace); })  // (return (11.25/2 =) 5.625) + i * (5.625) 
      .text(function(d) { return d.name; });      

}); // End Data callback function

  function findMaxY(data){  // Define function "findMaxY"
    var maxYValues = data.map(function(d) { 
      if (d.visible){
        return d3.max(d.values, function(value) { // Return max rating value
          return value.rating; })
      }
    });
    return d3.max(maxYValues);
  }

 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(data)
  .enter()
  .append("g")
  .attr("class", "mouse-per-line");

mousePerLine.append("circle")
  .attr("r", 7)
  .style("stroke", function(d, i) {
    return z(i);
  })
  .style("fill", "none")
  .style("stroke-width", "1px")
  .style("opacity", "0");

mousePerLine.append("rect")
    .attr("x", 0)
    .attr("y", -10)
  .attr("width", 85)
    .attr("height", 35)
  .style("stroke", function(d, i) {
    return z(i);
  })
    .attr("class", "tooltip-container")
  .style("fill", "red")
    .style("opacity", "0")
  .style("stroke-width", "1px");


mousePerLine.append("text").attr("id", "x-text")
  .attr("transform", "translate(10,3)");
mousePerLine.append("text").attr("id", "y-text")
  .attr("transform", "translate(10,23)");


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 circle")
      .style("opacity", "0");
      d3.selectAll(".mouse-per-line rect")
      .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 circle")
      .style("opacity", "1");
    d3.selectAll(".mouse-per-line text")
      .style("opacity", "1");
        d3.selectAll(".mouse-per-line rect")
      .style("opacity", "0.2");             
  })
  .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[0];
          }).right;
        var idx = bisect(d.data, xDate);

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

        while (true) {
          target = Math.floor((beginning + end) / 2);
          var 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('#y-text')
          .text("y: " + y.invert(pos.y).toFixed(2));

                d3.select(this).select('#x-text')
          .text("x: " + d3.time.format("%X")(xDate));

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

0 个答案:

没有答案