D3 Multi Line With Mouse Over Cursor For Both Y And X Value
For this working example (see Multiseries line chart with mouseover tooltip), I would need to display also the x axis value (and not only the y value) next to the circles. How can
Solution 1:
Inside your mousemove
function, you already have the date in the variable xDate
.
Thus, all you need is a new time format:
var parseDate2 = d3.time.format("%Y/%m/%d");
And use xDate
in the text function:
.text(y.invert(pos.y).toFixed(2) + " - " + parseDate2(xDate));
Here is the modified code:
<!DOCTYPE html><html><head><scriptdata-require="d3@3.5.3"data-semver="3.5.3"src="//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 parseDate2 = d3.time.format("%Y/%m/%d");
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
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) {
returnx(d.date);
})
.y(function(d) {
returny(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) {
returncolor(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) {
returnline(d.values);
})
.style("stroke", function(d) {
returncolor(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("circle")
.attr("r", 7)
.style("stroke", function(d) {
returncolor(d.name);
})
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");
mousePerLine.append("text")
.attr("transform", "translate(10,3)");
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 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 canvasvar 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) {
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;
elseif (pos.x < mouse[0]) beginning = target;
elsebreak; //position found
}
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2) + " - "
+ parseDate2(xDate));
return"translate(" + mouse[0] + "," + pos.y +")";
});
});
</script></body></html>
Post a Comment for "D3 Multi Line With Mouse Over Cursor For Both Y And X Value"