多鼠标线图与鼠标悬停工具提示 [英] Multiseries line chart with mouseover tooltip
问题描述
我使用
现在,我想添加一个x值mouseover工具提示,当您悬停其垂直位置时,每行的工具提示。像这,但多行。
我发现此StackOverflow答案(包括 JSFiddle ),但我似乎无法使其与我的多联盟
svg.append(path)//这是鼠标后面的黑色垂直线
.attr(class,mouseLine)
.style(stroke,black)
.style(stroke-width,1px)
.style opacity,0);
var mouseCircle = causation.append(g)//为每一行添加组来保存文本和圆
.attr(class,mouseCircle);
mouseCircle.append(circle)//添加一个圆圈以跟随路径
.attr(r,7)
.style (fill,none)
.style(stroke-width, 1px);
mouseCircle.append(text)
.attr(transform,translate(10,3) // text to hold coordinates
var bisect = d3.bisector(function(d){return d.YEAR;})right; //可重用的bisect在之前/之后找到点
svg.append('svg:rect')//附加一个rect来捕捉canvas上的鼠标移动
.attr('width' ,width)//不能在ag元素上捕获鼠标事件
.attr('height',height)
.attr('fill','none')
.attr指针事件','所有')
.on('mouseout',function(){//鼠标移出时隐藏线,圆和文本
d3.select(mouseLine)
.style(opacity,0);
d3.selectAll(mouseCircle circle)
.style(opacity,0);
d3。 selectAll(。mouseCircle text)
.style(opacity,0);
})
.on('mouseover',function显示线,圆和文本
d3.select(。mouseLine)
.style(opacity,1);
d3.selectAll(mouseCircle circle)
.style(opacity,1);
d3.selectAll(mouseCircle text)
.style )
.on('mousemove',function(){//鼠标移动到画布上
d3.select(mouseLine)
.attr(d,function
yRange = y.range(); // y轴范围
var xCoor = d3.mouse(this)[0]; //鼠标在x中的位置
var xDate = x.invert(xCoor); //对应于鼠标的日期x
d3.selectAll('。mouseCircle')//对于每个圆形组
.each(function(d,i){
var rightIdx = bisect [1] .values,xDate); //在鼠标右键的数据中找到日期
var interSect = get_line_intersection(xCoor,//获取垂直线和数据线的交叉
yRange [0 ],
xCoor,
yRange [1],
x(data [i] .values [rightIdx-1] .YEAR),
y(data [i] .values [rightIdx -1] .VALUE),
x(data [i] .values [rightIdx] .YEAR),
y(data [i] .values [rightIdx] .VALUE));
d3.select(this)//将圆移动到交集
.attr('transform','translate('+ interSect.x +','+ interSect.y +')');
d3.select(this.children [1])//写坐标出
.text(xDate.toLocaleDateString()+,+ y.invert(interSect.y).toFixed 0));
});
returnM+ xCoor +,+ yRange [0] +L+ xCoor +,+ yRange [1] // position vertical line
});
});
//从这里开始:http://stackoverflow.com/a/1968345/16363
function get_line_intersection(p0_x,p0_y,p1_x,p1_y,
p2_x,p2_y,p3_x ,p3_y)
{
var rV = {};
var s1_x,s1_y,s2_x,s2_y;
s1_x = p1_x - p0_x; s1_y = p1_y-p0_y;
s2_x = p3_x - p2_x; s2_y = p3_y-p2_y;
var s,t;
s =(-s1_y *(p0_x-p2_x)+ s1_x *(p0_y-p2_y))/(-s2_x * s1_y + s1_x * s2_y)
t =(s2_x *(p0_y-p2_y)-s2_y *(p0_x-p2_x))/(-s2_x * s1_y + s1_x * s2_y)
if(s> = 0&& s< = 1&& t> = 0&& t< = 1)
{
//检测到碰撞
rV.x = p0_x +(t * s1_x);
rV.y = p0_y +(t * s1_y);
}
return rV;
}
所以,简单来说,我想把折线图JSFiddle 与此 tooltip JSFiddle 。有人知道如何这样做吗?或者有一个更容易的方法来创建这样的工具提示?
您参考的问题我在四月回答。从那时起,我学到了更多关于 SVG
和 d3
的信息,因此我将其作为更新到该答案。
请注意,我从@ Duopixel的优秀代码示例中借用了一些代码此处。
以下是注释的细节:
鼠标在无意义
var mouseG = svg.append(g)
.attr(class,mouse-over-effects);
//这是垂直线
mouseG.append(path)
.attr(class,mouse-line)
.style (stroke,black)
.style(stroke-width,1px)
.style(opacity,0);
//保存对所有行的引用
var lines = document.getElementsByClassName('line');
//这里的每个圆圈和文本行的
var mousePerLine = mouseG.selectAll('。鼠标每行')
.data(城市)
.enter()
.append(g)
.attr(class,mouse-per-line);
//圆圈
mousePerLine.append(circle)
.attr(r,7)
.style d){
return color(d.name);
})
.style(fill,none)
.style(stroke-width 1px)
.style(opacity,0);
//文本
mousePerLine.append(text)
.attr(transform,translate(10,3)
// rect捕获鼠标移动
mouseG.append('svg:rect')
.attr('width',width)
.attr height',height)
.attr('fill','none')
.attr('pointer-events','all')
.on ){//鼠标离开隐藏线,圆和文本
d3.select(鼠标线)
.style(opacity,0);
d3。 selectAll(。mouse-per-line circle)
.style(opacity,0);
d3.selectAll(鼠标每行文本)
.style(opacity,0);
})
.on('mouseover',function(){//鼠标在显示行,圆和文本
d3。 select(。mouse-line)
.style(opacity,1);
d3.selectAll(鼠标每行圆)
.style opacity,1);
d3.selectAll(mouse-per-line text)
.style(opacity,1);
} b $ b .on('mousemove',function(){//鼠标移动到画布上
var mouse = d3.mouse(this);
//移动垂直线
d3.select(。mouse-line)
.attr(d,function(){
var d =M+ mouse [0] +
d + =+ mouse [0] +,+ 0;
return d;
});
//定位圆和文本
d3.selectAll(鼠标每行)
.attr(transform,function(d,i){
console.log(width / mouse [0])
var xDate = x.invert(mouse [0]),
bisect = d3.bisector(function(d){return d。 date;}); right;
idx = bisect(d.values,xDate);
//因为我们使用曲线拟合,我们不能继续寻找点完成在我最后的答案
//这使用一些SVG路径函数进行搜索
//找到正确的位置
//从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])begin = target;
else break; // position found
}
//使用y值更新文本
d3.select(this).select('text')
.text .invert(pos.y).toFixed(2));
//返回位置
returntranslate(+ mouse [0] +,+ pos.y +)
});
});
完整的工作代码:
<!DOCTYPE html>< html>< head> < script data-require =d3@3.5.3data-semver =3.5.3src =// 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-width:1.5px; }< / style>< / head>< body> < script> var myData =date New York San Francisco Austin \\\
\20111001 63.4 62.7 72.2 \\\
\20111002 58.0 59.9 67.7\\\
\20111003 53.3 59.1 69.4\\20111004 55.7 58.8 68.0 \\\
\\ \\20111005 64.2 58.7 72.4 \\\
\20111006 58.8 57.0 77.0\\\
\20111007 57.9 56.7 82.3 \\\
\20111008 61.8 56.8 78.9\\\\20111009 69.3 56.7 68.8 \\\\ 20111110 71.2 60.1 68.7 \\\
\20111011 68.7 61.1 70.3 \\\
\20111012 61.8 61.5 75.3 \\\
\20111013 63.0 64.3 76.6 \\\
\20111014 66.9 67.1 66.6 \\\\20111015 61.7 64.6 68.0 \\\
\ 20111016 61.8 61.6 70.6 \\\
\20111017 62.8 61.1 71.1 \\\
\20111018 60.8 59.2 70.0 \\20111019 62.1 58.9 61.6 \\\\20111020 65.1 57.2 57.4\\\\\\\ 20111121 55.6 56.4 64.3 \\ \\ n\20111022 54.4 60.7 72.4\\\
; 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 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){return x(d.date);})。 ;}); var svg = d3.select(body)。append(svg).attr(width,width + margin.left + margin.right).attr(height,height + margin.top + 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 ){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){return color(d.name);}); legend.append('text').attr('x',width -8).attr('y',function(d,i){return(i * 20)+ 9;}) ){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) y,6).attr(dy,.71em).style(text-anchor,end).text 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 d){return color(d.name);}); city.append(text).datum(function(d){return {name:d.name,value:d.values [d.values.length - 1]};}).attr (d){returntranslate(+ 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)//这是跟随鼠标的黑色垂直线.attr(class,mouse-line).style(stroke,black).style ,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)); mouse.get('svg:rect')//追加一个Rect来捕捉画布上的鼠标移动.attr('width',width)//无法捕捉ag元素上的鼠标事件.attr('height',height) .attr('fill','none').attr('pointer-events','all').on('mouseout',function(){//鼠标离开隐藏线,圆和文本d3.select .mouse-line).style(opacity,0); d3.selectAll(mouse-per-line circle).style(opacity,0); d3.selectAll (mouseover,function(){//在鼠标在显示行,圆和文本d3.select(鼠标单击每行文本).style(opacity,0 .mouse-line).style(opacity,1); d3.selectAll(mouse-per-line circle).style(opacity,1); d3.selectAll鼠标每行文本).style(opacity,1);}).on('mousemove',function(){//鼠标移动在画布var mouse = d3.mouse .select(。mouse-line).attr(d,function(){var d =M+ mouse [0] +,+ height; d + =+ mouse [0] +,+ 0; return d; }); d3.selectAll(鼠标每行).attr(transform,function(d,i){console.log(width / mouse [0])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 = 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])begin = target; else break; // position found} d3.select(this).select('text').text(y.invert(pos.y).toFixed(2)); returntranslate 0] +,+ pos.y +); }); }); < / script>< / body>< / html>
I've created a multi-series line chart using this bl.ocks.org code example. I've managed to recreate it on JSFiddle.
Now, I'm trying to add an x-value mouseover tooltip, which displays the a tooltip at each line when you hover its vertical position. Something like this, but for multiple lines.
I found this StackOverflow answer (it includes a JSFiddle), but I can't seem to make it work with my multiseries line chart.
svg.append("path") // this is the black vertical line to follow mouse
.attr("class","mouseLine")
.style("stroke","black")
.style("stroke-width", "1px")
.style("opacity", "0");
var mouseCircle = causation.append("g") // for each line, add group to hold text and circle
.attr("class","mouseCircle");
mouseCircle.append("circle") // add a circle to follow along path
.attr("r", 7)
.style("stroke", function(d) { console.log(d); return color(d.key); })
.style("fill","none")
.style("stroke-width", "1px");
mouseCircle.append("text")
.attr("transform", "translate(10,3)"); // text to hold coordinates
var bisect = d3.bisector(function(d) { return d.YEAR; }).right; // reusable bisect to find points before/after line
svg.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(".mouseLine")
.style("opacity", "0");
d3.selectAll(".mouseCircle circle")
.style("opacity", "0");
d3.selectAll(".mouseCircle text")
.style("opacity", "0");
})
.on('mouseover', function(){ // on mouse in show line, circles and text
d3.select(".mouseLine")
.style("opacity", "1");
d3.selectAll(".mouseCircle circle")
.style("opacity", "1");
d3.selectAll(".mouseCircle text")
.style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
d3.select(".mouseLine")
.attr("d", function(){
yRange = y.range(); // range of y axis
var xCoor = d3.mouse(this)[0]; // mouse position in x
var xDate = x.invert(xCoor); // date corresponding to mouse x
d3.selectAll('.mouseCircle') // for each circle group
.each(function(d,i){
var rightIdx = bisect(data[1].values, xDate); // find date in data that right off mouse
var interSect = get_line_intersection(xCoor, // get the intersection of our vertical line and the data line
yRange[0],
xCoor,
yRange[1],
x(data[i].values[rightIdx-1].YEAR),
y(data[i].values[rightIdx-1].VALUE),
x(data[i].values[rightIdx].YEAR),
y(data[i].values[rightIdx].VALUE));
d3.select(this) // move the circle to intersection
.attr('transform', 'translate(' + interSect.x + ',' + interSect.y + ')');
d3.select(this.children[1]) // write coordinates out
.text(xDate.toLocaleDateString() + "," + y.invert(interSect.y).toFixed(0));
});
return "M"+ xCoor +"," + yRange[0] + "L" + xCoor + "," + yRange[1]; // position vertical line
});
});
// from here: http://stackoverflow.com/a/1968345/16363
function get_line_intersection(p0_x, p0_y, p1_x, p1_y,
p2_x, p2_y, p3_x, p3_y)
{
var rV = {};
var s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x; s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x; s2_y = p3_y - p2_y;
var s, t;
s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
{
// Collision detected
rV.x = p0_x + (t * s1_x);
rV.y = p0_y + (t * s1_y);
}
return rV;
}
So, to put it simply, I want to combine my line chart JSFiddle with this tooltip JSFiddle. Does anybody know how to do this? Or is there an easier way to create a tooltip like this? Any help is appreciated!
The question you referenced I answered back in April. Since then, I've learned a bit more about SVG
and d3
, so I'll let this serve as an update to that answer.
Note, I borrowed a bit of code from @Duopixel's excellent code sample here.
Here's the commented particulars:
// append a g for all the mouse over nonsense
var mouseG = svg.append("g")
.attr("class", "mouse-over-effects");
// this is the vertical line
mouseG.append("path")
.attr("class", "mouse-line")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "0");
// keep a reference to all our lines
var lines = document.getElementsByClassName('line');
// here's a g for each circle and text on the line
var mousePerLine = mouseG.selectAll('.mouse-per-line')
.data(cities)
.enter()
.append("g")
.attr("class", "mouse-per-line");
// the circle
mousePerLine.append("circle")
.attr("r", 7)
.style("stroke", function(d) {
return color(d.name);
})
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");
// the text
mousePerLine.append("text")
.attr("transform", "translate(10,3)");
// rect to 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 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);
// move the vertical line
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
// position the circle and text
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.date; }).right;
idx = bisect(d.values, xDate);
// since we are use curve fitting we can't relay on finding the points like I had done in my last answer
// this conducts a search using some SVG path functions
// to find the correct position on the line
// from 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 the text with y value
d3.select(this).select('text')
.text(y.invert(pos.y).toFixed(2));
// return position
return "translate(" + mouse[0] + "," + pos.y +")";
});
});
Full working code:
<!DOCTYPE html>
<html>
<head>
<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>
<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 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) {
return x(d.date);
})
.y(function(d) {
return y(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) {
return color(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) {
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.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) {
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.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;
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>
</body>
</html>
这篇关于多鼠标线图与鼠标悬停工具提示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!