Jam*_*Lim 2 javascript charts svg d3.js
我对 D3 很陌生,只是将以下工具提示合并到我的应用程序中。我有一个单折线图和一个多折线图。
单行:https : //bl.ocks.org/alandunning/cfb7dcd7951826b9eacd54f0647f48d3
Multi Line:带有鼠标悬停工具提示的多系列折线图
如您所见,这两个工具提示的功能是不同的。Single Line 工具提示从每个数据点跳出,而 Multi Line 则继续跟随图表路径。我想更改多行功能以模仿单行工具提示的工作方式。
任何帮助将不胜感激。如果我需要提供更多信息,请告诉我。另请注意,我正在处理的数据是一组数组
下面是我的代码:
单线图:
let g = svg.append('g');
g.append("path")
.datum(this.dataObj)
.attr("class",`line-${this.yAxisData} line`)
.attr('d', line)
.attr("stroke",`${this.color(this.dataObj.label)}`)
.attr("fill",'none')
.attr("transform", `translate(${this.margin.left},${this.margin.top})`);
var focus = g.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("line")
.datum(this.dataObj)
.attr("class", "x-hover-line hover-line")
.attr("transform",`translate(${this.margin.left},${this.margin.top})`)
.attr("stroke",`${this.color(this.dataObj.label)}`)
.attr("y1", 0)
.attr("y2", height);
focus.append("circle")
.datum(this.dataObj)
.attr("transform",`translate(${this.margin.left},${this.margin.top})`)
.attr("stroke",`${this.color(this.dataObj.label)}`)
.attr("r", 7.5);
focus.append("text")
.attr("class","linetip")
.attr("x", 40)
.attr("dy", "0.5em");
svg.append("rect")
.attr("transform", `translate(${this.margin.left},${this.margin.top})`)
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", this.mousemove);
mousemove() {
var bisectDate = d3.bisector(function(d) { return d.date; }).left;
let mouse = d3.mouse(d3.event.currentTarget);
let svg = d3.select(this.container);
var x0 = this.x.invert(mouse[0]);
var i = bisectDate(this.dataObj, x0);
var d0 = this.dataObj[i - 1];
var d1 = this.dataObj[i];
var d = x0 - d0.date > d1.date - x0 ? d1 : d0;
var focus = svg.select(".focus");
focus.attr("transform", "translate(" + this.x(d[this.xAxisData]) + "," + this.y(d[this.yAxisData]) + ")");
focus.select("text").text(`[${d[this.yAxisData]}]`);
focus.select(".x-hover-line").attr("y2", this.height - this.y(d[this.yAxisData]));
focus.select(".y-hover-line").attr("x2", this.width + this.width);
}
Run Code Online (Sandbox Code Playgroud)
多线图:
//append paths
let g = svg.append('g');
let chartLines = g.selectAll('.lines')
.data(this.dataObj)
.enter()
.append('g')
.attr('class', 'lines');
chartLines.append('path')
.attr('class','line')
.attr('d', d => {
return line(d);
})
.attr('stroke', (d) => color(d[0].label))
.attr('fill','none')
.attr("transform", `translate(${this.margin.left},0)`);
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", "2px")
.style("stroke-dasharray", "3,3")
.style("opacity", "0");
var mousePerLine = mouseG.selectAll('.mouse-per-line')
.data(this.dataObj)
.enter()
.append("g")
.attr("class", "mouse-per-line");
mousePerLine.append("circle")
.datum(d=>{return d})
.attr("r", 7)
.attr("stroke", (d,i) => {
console.log(d)
return `${this.color(d[i].label)}`
})
.style("fill", "none")
.style("opacity", "0");
mousePerLine.append("text")
.datum(d=>{return d})
.attr("transform", "translate(10,3)");
mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr("transform", `translate(${this.margin.left},0)`)
.attr('width', width) // can't catch mouse events on a g element
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', () => { // 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', () => { // 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', () => {
let mouse = d3.mouse(d3.event.currentTarget);
d3.select(".mouse-line")
.attr("d", () => {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
d3.selectAll(".mouse-per-line")
.attr("transform", (d, i) => {
var lines = document.getElementsByClassName('line')
var xDate = this.x.invert(mouse[0])
var bisect = d3.bisector(function(d) { return d.date; }).right;
var idx = bisect(this.dataObj, xDate);
var beginning = 0,
end = lines[i].getTotalLength()
var target = null;
while (true){
var target = Math.floor((beginning + end) / 2);
var 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('text')
.text(this.y.invert(pos.y));
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
Run Code Online (Sandbox Code Playgroud)
我将Mark的答案作为Multiseries 折线图的参考,其中包含您提供的鼠标悬停工具提示。
基本上,您需要做的是将工具提示设置为在 x 轴数据的每个刻度上显示,因此与其用鼠标 [0] 抓取鼠标的位置并移动工具提示,您应该将其移动到x 轴数据是。
这是我所做更改的详细信息:
mouseG.append('svg:rect')
.attr('width', width)
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', () => {
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', () => {
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', () => {
let mouse = d3.mouse(d3.event.currentTarget);
// MOVE THIS BEFORE THE RETURN
// d3.select(".mouse-line")
// .attr("d", () => {
// var d = "M" + mouse[0] + "," + height;
// d += " " + mouse[0] + "," + 0;
// return d;
// });
d3.selectAll(".mouse-per-line")
.attr("transform", (d, i) => {
var lines = document.getElementsByClassName('line')
var xDate = this.x.invert(mouse[0])
var bisect = d3.bisector(function(d) { return d.date; }).right;
var idx = bisect(this.dataObj, xDate);
// GET RID OF THIS
// var beginning = 0,
// end = lines[i].getTotalLength()
// var target = null;
// while (true){
// var target = Math.floor((beginning + end) / 2);
// var 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
// }
// REPLACE pos.y WITH y(d.values[idx].temperature)
// AND mouse[0] WITH x(d.values[idx].date)
d3.select('text')
.text(this.y.invert(pos.y));
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
Run Code Online (Sandbox Code Playgroud)
下面是应用了更改的完整工作代码。对于这个片段,我曾经interpolate('linear')正确地显示值;如果使用interpolate('basis'),则工具提示和线条将无法正确匹配:
mouseG.append('svg:rect')
.attr('width', width)
.attr('height', height)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', () => {
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', () => {
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', () => {
let mouse = d3.mouse(d3.event.currentTarget);
// MOVE THIS BEFORE THE RETURN
// d3.select(".mouse-line")
// .attr("d", () => {
// var d = "M" + mouse[0] + "," + height;
// d += " " + mouse[0] + "," + 0;
// return d;
// });
d3.selectAll(".mouse-per-line")
.attr("transform", (d, i) => {
var lines = document.getElementsByClassName('line')
var xDate = this.x.invert(mouse[0])
var bisect = d3.bisector(function(d) { return d.date; }).right;
var idx = bisect(this.dataObj, xDate);
// GET RID OF THIS
// var beginning = 0,
// end = lines[i].getTotalLength()
// var target = null;
// while (true){
// var target = Math.floor((beginning + end) / 2);
// var 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
// }
// REPLACE pos.y WITH y(d.values[idx].temperature)
// AND mouse[0] WITH x(d.values[idx].date)
d3.select('text')
.text(this.y.invert(pos.y));
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5530 次 |
| 最近记录: |