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:

enter image description here

and here how appear (pardon horrible paint skills): enter image description here

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