具有鼠标hover工具提示的多重系列折线图
我使用这个bl.ocks.org代码示例创建了一个多系列折线图。 我已经设法在JSFiddle上重新创建它。
现在,我正在尝试添加一个x值鼠标hover工具提示,当您hover其垂直位置时,它会显示每行的工具提示。 这样的东西,但多行。
我发现这个StackOverflow答案 (它包括一个JSFiddle ),但我似乎无法使它与我的多重系列折线图一起工作。
svg.append("path") // this is the black vertical line to follow mouse .attr("class","mouseLine") .style("stroke","black") .style("stroke-width", "1px") .style("opacity", "0"); var mouseCircle = causation.append("g") // for each line, add group to hold text and circle .attr("class","mouseCircle"); mouseCircle.append("circle") // add a circle to follow along path .attr("r", 7) .style("stroke", function(d) { console.log(d); return color(d.key); }) .style("fill","none") .style("stroke-width", "1px"); mouseCircle.append("text") .attr("transform", "translate(10,3)"); // text to hold coordinates var bisect = d3.bisector(function(d) { return d.YEAR; }).right; // reusable bisect to find points before/after line svg.append('svg:rect') // append a rect to catch mouse movements on canvas .attr('width', width) // can't catch mouse events on ag element .attr('height', height) .attr('fill', 'none') .attr('pointer-events', 'all') .on('mouseout', function(){ // on mouse out hide line, circles and text d3.select(".mouseLine") .style("opacity", "0"); d3.selectAll(".mouseCircle circle") .style("opacity", "0"); d3.selectAll(".mouseCircle text") .style("opacity", "0"); }) .on('mouseover', function(){ // on mouse in show line, circles and text d3.select(".mouseLine") .style("opacity", "1"); d3.selectAll(".mouseCircle circle") .style("opacity", "1"); d3.selectAll(".mouseCircle text") .style("opacity", "1"); }) .on('mousemove', function() { // mouse moving over canvas d3.select(".mouseLine") .attr("d", function(){ yRange = y.range(); // range of y axis var xCoor = d3.mouse(this)[0]; // mouse position in x var xDate = x.invert(xCoor); // date corresponding to mouse x d3.selectAll('.mouseCircle') // for each circle group .each(function(d,i){ var rightIdx = bisect(data[1].values, xDate); // find date in data that right off mouse var interSect = get_line_intersection(xCoor, // get the intersection of our vertical line and the data line yRange[0], xCoor, yRange[1], x(data[i].values[rightIdx-1].YEAR), y(data[i].values[rightIdx-1].VALUE), x(data[i].values[rightIdx].YEAR), y(data[i].values[rightIdx].VALUE)); d3.select(this) // move the circle to intersection .attr('transform', 'translate(' + interSect.x + ',' + interSect.y + ')'); d3.select(this.children[1]) // write coordinates out .text(xDate.toLocaleDateString() + "," + y.invert(interSect.y).toFixed(0)); }); return "M"+ xCoor +"," + yRange[0] + "L" + xCoor + "," + yRange[1]; // position vertical line }); }); // from here: https://stackoverflow.com/a/1968345/16363 function get_line_intersection(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) { var rV = {}; var s1_x, s1_y, s2_x, s2_y; s1_x = p1_x - p0_x; s1_y = p1_y - p0_y; s2_x = p3_x - p2_x; s2_y = p3_y - p2_y; var s, t; s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y); t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y); if (s >= 0 && s = 0 && t <= 1) { // Collision detected rV.x = p0_x + (t * s1_x); rV.y = p0_y + (t * s1_y); } return rV; }
所以,简单地说,我想将我的折线图JSFiddle与这个工具提示JSFiddle结合起来。 有人知道怎么做这个吗? 或者是否有更简单的方法来创建这样的工具提示? 任何帮助表示赞赏!
你引用的问题我在四月回答。 从那以后,我学到了更多关于SVG
和d3
,所以我将把它作为对这个答案的更新。
注意,我从@ Duopixel的优秀代码示例中借用了一些代码。
这是评论的细节:
// append ag for all the mouse over nonsense var mouseG = svg.append("g") .attr("class", "mouse-over-effects"); // this is the vertical line mouseG.append("path") .attr("class", "mouse-line") .style("stroke", "black") .style("stroke-width", "1px") .style("opacity", "0"); // keep a reference to all our lines var lines = document.getElementsByClassName('line'); // here's ag for each circle and text on the line var mousePerLine = mouseG.selectAll('.mouse-per-line') .data(cities) .enter() .append("g") .attr("class", "mouse-per-line"); // the circle mousePerLine.append("circle") .attr("r", 7) .style("stroke", function(d) { return color(d.name); }) .style("fill", "none") .style("stroke-width", "1px") .style("opacity", "0"); // the text mousePerLine.append("text") .attr("transform", "translate(10,3)"); // rect to capture mouse movements mouseG.append('svg:rect') .attr('width', width) .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 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"); }) .on('mousemove', function() { // mouse moving over canvas var mouse = d3.mouse(this); // move the vertical line d3.select(".mouse-line") .attr("d", function() { var d = "M" + mouse[0] + "," + height; d += " " + mouse[0] + "," + 0; return d; }); // position the circle and text 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); // since we are use curve fitting we can't relay on finding the points like I had done in my last answer // this conducts a search using some SVG path functions // to find the correct position on the line // from http://bl.ocks.org/duopixel/3824661 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 } // update the text with y value d3.select(this).select('text') .text(y.invert(pos.y).toFixed(2)); // return position return "translate(" + mouse[0] + "," + pos.y +")"; }); });
完整的工作代码: