多鼠标线图与鼠标悬停工具提示 [英] Multiseries line chart with mouseover tooltip

查看:141
本文介绍了多鼠标线图与鼠标悬停工具提示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用



现在,我想添加一个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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆