javascript - 单击按钮 d3.js 时如何在线删除工具提示

标签 javascript d3.js

我正在尝试在this example之后的多折线图表上添加工具提示。 我希望当鼠标悬停在其中任何一行上时,工具提示同时显示在这两行上,例如 example here 。在我的真实案例中,我有四行。

这是我的 jsbins https://jsbin.com/budonapeki/edit?html,js,console,output 点击右上角的“Run with JS”即可查看图表。

我的问题是...

  1. 在我的 jsbin 中,工具提示仅显示在该行的第一个点上,当鼠标移动时其他工具提示不会显示。谁能帮忙指出原因吗?

  2. 如何让工具提示同时显示在各行上而不重复太多代码?由于我使用 d3.nest() 来转换数组,因此我不确定这是否会影响工具提示的工作。

欣赏!

已更新

按照马克评论中的链接,我终于完成了工具提示。 但我还有一些其他问题...

这是我更新的 JSbins https://jsbin.com/hoceneneso/edit?html,js,console,output

第一个问题是..在我的图表中,右侧有两个按钮,当单击按钮时,线条可以消失或出现。并且该线对应的工具提示应该消失或与该线一起出现。但在我的图表中,即使我单击按钮,工具提示仍然存在。

我试图删除并更改工具提示的不透明度,但我仍然无法使其工作。有人对此有想法吗?

第二个问题是..我试图让工具提示开始显示在“name1”处,这是线条的起点。我知道灰色矩形是为了捕获 Canvas 上的鼠标移动,因此我尝试通过 .attr("transform", "translate(180,3)") 移动矩形,但工具提示仍然显示将鼠标悬停在 y 轴上。任何人都可以解释原因和建议吗?

非常感谢!

最佳答案

第一问题,为每个“每行鼠标”设置一个唯一的 ID,以便您可以切换其不透明度:

var mousePerLine = mouseG.selectAll('.mouse-per-line')
  .data(dataNest1)
  .enter()
  .append("g")
  .attr("class", "mouse-per-line")
  .attr("id", function(d){
    return "mouse-per-line-" + d.key;
  });   

在图例中单击处理程序:

d3.select("#mouse-per-line-" + d.key)
  .style("opacity", newOpacity);

第二问题,设置xwidth属性,而不是transform来移动矩形。您可以通过以下方式使其动态化:

mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas
  .attr('x', x(dataNest1[0].values[0].x))
  .attr('width', x(dataNest1[0].values[dataNest1[0].values.length - 1].x) - x(dataNest1[0].values[0].x)) 
  ...
<小时/>

更新的代码:

var data1 = [
	     {x: "Name1", y: 2.5, label: "A"},
		 {x: "Name2", y: 3.5, label: "A"},
	     {x: "Name3", y: 4.7, label: "A"},
	     {x: "Name1", y: 4.7, label: "B"},
		 {x: "Name2", y: 3.5, label: "B"},
	     {x: "Name3", y: 4.9, label: "B"},
	 ];	 
	var margin = {top: 20, right: 150, bottom: 60, left: 80},
		width = 1160 - margin.left - margin.right,
		height = 500 - margin.top - margin.bottom;	
	var x = d3.scale.ordinal().
		rangeBands([0, width], 0.4, 0.8);
	var y = d3.scale.linear()
		.range([height, 0]);
	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.x); })
		.y(function(d) { return y(d.y); });
	var svg = d3.select("#lineChart").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 + ")");
	x.domain(data1.map(function(d) { return d.x; }));
	y.domain([0, d3.max(data1, function(d) { return d.y; })]);	
	svg.append("g").
      attr("class", "x axis").
      attr("transform", "translate(-70," + height + ")").
      call(xAxis);
    svg.append("g").
      attr("class", "y axis").
      call(yAxis);
	  
	var dataNest1 = d3.nest()
        .key(function(d) {return d.label;})
        .entries(data1);
		//console.log(dataNest1)
	 var color = d3.
        scale.
        ordinal().
        range(['red', 'blue']).
        domain(d3.keys(data1[0]).
        filter(function(key) {return key === 'label';}));	
	 var legendSpace = width/dataNest1.length;	
		
	   dataNest1.forEach(function(d,i) { 
	   
        svg.append("path")
            .attr("class", "line1")
            .style("stroke", function() {
                return d.color = color(d.key); })
            .attr("id", 'tag'+d.key.replace(/\s+/g, '')) // assign ID **
            .attr("d", line(d.values));
			
        svg.append("text")
            .attr("x", width - margin.left + 50)
            .attr("y", legendSpace/4 + i*(legendSpace/6))
            .attr("class", "lineLegend1")
			.attr("id", 'tagLegend'+d.key.replace(/\s+/g, '')) // assign ID **
            .style("fill", function() {
                return d.color = color(d.key); })
            .on("click", function(){               
                console.log(d);
                // Determine if current line is visible 
                var active   = d.active ? false : true,  
                newOpacity = active ? 0 : 1;         
                // Hide or show the elements based on the ID
                d3.select("#tag"+d.key.replace(/\s+/g, ''))
				//.remove(); 
                    .transition().duration(500)         
                    .style("opacity", newOpacity);
				//d3.selectAll(".mouse-per-line circle")
				//	.style("opacity", newOpacity);
				//d3.selectAll(".mouse-per-line text")
				//	.style("opacity", newOpacity);					
                // Update whether or not the elements are active
                d.active = active; 
                d3.select("#mouse-per-line-" + d.key)
                  .style("opacity", newOpacity);
          
                })
            .text(d.key); 
	
		 });	    	
	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('line1');

    var mousePerLine = mouseG.selectAll('.mouse-per-line')
      .data(dataNest1)
      .enter()
      .append("g")
      .attr("class", "mouse-per-line")
      .attr("id", function(d){
        return "mouse-per-line-" + d.key;
      });	
	
    mousePerLine.append("circle")
      .attr("r", 7)
      .style("stroke", function(d) {
        return color(d.key);
      })
      .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('x', x(dataNest1[0].values[0].x))
      .attr('width', x(dataNest1[0].values[dataNest1[0].values.length - 1].x) - x(dataNest1[0].values[0].x)) 
      .attr('height', height)
      .attr('fill', 'grey')
	  .style('opacity', '0.4')
      .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 xQuater = y.invert(d3.mouse(this)[0]),
                bisect = d3.bisector(function(d) { return d.x; }).right;
                idx = bisect(d.values, xQuater);
            
            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 +")";
          });
      });
<!DOCTYPE html>
<html>
  <head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
    <meta charset='utf-8'>
    <title>Charts</title>  
    </head>
  <body>
    <div align="center" id="lineChart">
    </div>  
  <style>
  .axis {
  	font-family: Helvetica;
	font-size: 1em;
	font-weight: bold;
	color: #444444;
}
.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.line2{
  fill: none;
  stroke: red;
  stroke-width: 1.5px;
}
.line1{
  fill: none;
  stroke: blue;
  stroke-width: 1.5px;
} 
  </style>
  </body>
</html>

关于javascript - 单击按钮 d3.js 时如何在线删除工具提示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35441624/

相关文章:

javascript - D3 : Directional graph similar to tree layout but with back links

javascript - 无法在 Firefox 中选择对话框标题中的输入元素

javascript - 如何在基类代码内引用 `this.constructor` 上的静态属性?

javascript - 如何向 ActiveResource 添加 header 以创建 API 请求

javascript - 如何将 $.get 中的数据分配给变量,或读取responseText

javascript - 将平面嵌套数据转换为单个数组

javascript - 过渡期间的火灾事件

javascript - AngularJS + d3js : Issues with resizing objects

javascript - 在选择某些东西之前隐藏 d3.chart 的详细信息

javascript - 如何获取沿饼图边缘的切片坐标?