javascript - d3.js v4, how do I have a line follow the mouse on hover, but also have a circle follow the path? -
here js fiddle: https://jsfiddle.net/dernalia/3wzlv9yg/1/
i've been trying interpret code here: multiseries line chart mouseover tooltip, can't seem working.
this have far -- it's pretty copy paste.
// append g mouse on nonsense var mouseg = svg.append("g") .attr("class", "mouse-over-effects"); // vertical line mouseg.append("path") .attr("class", "mouse-line") .style("stroke", "black") .style("stroke-width", "1px") .style("opacity", "0"); // keep reference our lines var lines = document.getelementsbyclassname('line'); // here's g each circle , text on line var mouseperline = mouseg.selectall('.mouse-per-line') .data(data) .enter() .append("g") .attr("class", "mouse-per-line"); // circle mouseperline.append("circle") .attr("r", 7) .style("stroke", function(d) { return 'red'; }) .style("fill", "none") .style("stroke-width", "1px") .style("opacity", "0"); // text mouseperline.append("text") .attr("transform", "translate(10,3)"); // rect 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 , 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 , 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 on canvas var mouse = d3.mouse(this); // move vertical line d3.select(".mouse-line") .attr("d", function() { var d = "m" + mouse[0] + "," + height; d += " " + mouse[0] + "," + 0; return d; }); // position circle , text d3.selectall(".mouse-per-line") .attr("transform", function(d, i) { console.log(width/mouse[0]) console.log(mouse[1]); var xdate = x.invert(mouse[0]), bisect = d3.bisector(function(d) { return d.x; }).right; idx = bisect(d.values, xdate); // since use curve fitting can't relay on finding points had done in last answer // conducts search using svg path functions // find correct position on line // 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 text y value //d3.select(this).select('text') // .text(y.invert(pos.y).tofixed(2)); d3.select(this).select('circle') .attr('cy', pos.x) .attr('cx', pos.y); // return position return "translate(" + mouse[0] + "," + pos.y +")"; }); });
in case goes wrong fiddle, here have currently:
and here how appear (pardon horrible paint skills):
my issue related error well. cannot read property 'length' of undefined.
updated fiddle: https://jsfiddle.net/3wzlv9yg/2/. there few things going awry:
line circles
var mouseperline = mouseg.selectall('.mouse-per-line') .data(data) .enter() .append("g") .attr("class", "mouse-per-line");
this statement adds new g
element every data point, rather every line. replace array length of number of lines element each line. example replace .data(data)
.data(d3.range(lines.length))
.
multiple techniques of circle location position
it looks you've combined 2 different techniques calculating y location of circles, 1 involving calculating data values, , other calculating svg elements.
the code calculates data values has these lines:
var xdate = x.invert(mouse[0]), bisect = d3.bisector(function(d) { return d.x; }).right; idx = bisect(d.values, xdate);
there's error in bisect(d.values, xdate);
d.values
not assigned anywhere. should bisect(data, xdate);
, may irrelevant isn't used anywhere else, since rest of function calculates y position svg paths. can rid of bisect , idx if you're using approach:
var xdate = x.invert(mouse[0]);
setting location
this should alleviate console errors, mouse circles still not track properly. that's because location of circle set twice:
d3.select(this).select('circle') .attr('cy', pos.x) .attr('cx', pos.y); // return position return "translate(" + mouse[0] + "," + pos.y +")";
these statements sets of g
element correct location, sets circle
offset of equal amount. need one. since current implementation sets transform
of g
element, it's easier keep 1 , rid of circle offset.
// return position return "translate(" + mouse[0] + "," + pos.y +")";
all changes here: https://jsfiddle.net/3wzlv9yg/2/
Comments
Post a Comment