我正在尝试与d3.js制作交互式折线图。我从两个csv文件(emissions.csv和gdp.csv)中获取数据,我希望当我在图表上用鼠标传递时,它会显示一种带有关于该行上相应点的信息的标签。现在我必须在线上传递鼠标以显示标签,我无法弄清楚如何做我想做的事情。我发现this示例显示了我想要的内容,但我无法理解一些代码行,我无法理解如何在我的图上使用它。
这是我的代码:
<!DOCTYPE html>
<meta charset="utf-8">
<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>
<body>
<script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<script>
var margin = {top: 20, right: 80, bottom: 30, left: 150},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.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) { return x(d.year); })
.y(function(d) { return y(d.emission); });
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 + ")");
d3.csv("europe_emission.csv", function(error, data) {
if (error) throw error;
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "year"; }));
var cities = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {year: d.year, emission: +d[name]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.year; }));
y.domain([
0,
d3.max(cities, function(c) { return d3.max(c.values, function(v) { return v.emission; }); })
]);
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("Emission (thousand metric tons of CO2)");
var city = svg.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(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.year) + "," + y(d.value.emission) + ")";
})
.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) {
return color(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 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.year; }).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;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2));
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
});
</script>
europe_emission.csv:
今年,UnitedKingdom的买家,意大利,法国,西班牙,德国
2012,483423,386666,368845,276636,821717
2011,464036,413379,364819,280922,810441
2010,504997,424993,391075,280377,829401
2009,487442,414809,381992,293732,785602
2008,536733,463695,400720,333181,851111
2007,554251,475436,407254,363744,848548
2006,561128,483533,416414,356712,873246
2005,561101,488078,425740,365478,861733
2004,563962,489367,422201,350071,881743
2003,562296,486559,420492,333168,893599
2002,551553,470530,412019,328878,890875
2001,567904,468283,416267,308786,907541
2000,556667,462277,415079,308026,891515
1999,548047,458824,418193,294901,887890
1998,555499,453524,426564,271515,915176
1997,550525,442371,404884,263303,923080
1996,575026,438303,411302,250543,951863
1995,553701,444943,398480,262860,930857
1994,562061,419903,391681,249451,932485
1993,568100,427170,391484,237253,948683
1992,581828,433867,413020,245814,957561
1991,598323,434156,423121,237179,1004735
1990,591499,434656,398769,227508,1042065
答案 0 :(得分:1)
这花了我太长时间才弄明白,但这是你的数据排序。我上面的链接假设x轴上升,你有下降,所以你的路径从右到左绘制向后。对数据进行排序,或将搜索功能更改为从右向左:
d3.selectAll(".mouse-per-line")
.attr("transform", function(d, i) {
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]) beginning = target; //<-- this was end = target
else if (pos.x < mouse[0]) end = target; //<-- this was beginning = target
else break;
}
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2));
return "translate(" + mouse[0] + "," + pos.y + ")";
});
修正代码here。