javascript - d3.js (v4) 带工具提示的散点图

标签 javascript d3.js tooltip

我正在 d3 中构建散点图可视化,但在显示点工具提示时遇到了一些小问题。数据以数组对象的形式存在,其中位置 0 的元素是 x坐标,位置 1 y坐标,位置2点cluster ,位置 3 为 cluster description

[
  {
    "points": [
      [
        -12.32, 
        15.74, 
        1, 
        "Cluster 5"
      ], ... , 
      [
        -15.38, 
        -3.97, 
        0, 
        "Cluster 2"
      ]
  }
]

这是我的 JS:

d3.json("data.json", function(data) {

  var points = data[0].points;

  var svg = d3.select("#cluster"),
      width = +svg.attr("width"),
      height = +svg.attr("height");

  var k = height / width,
      x0 = [-15, 15],
      y0 = [-15 * k, 15 * k],
      x = d3.scaleLinear().domain(x0).range([0, width]),
      y = d3.scaleLinear().domain(y0).range([height, 0]),
      z = d3.scaleOrdinal(d3.schemeCategory10);

  var xAxis = d3.axisTop(x).ticks(12),
      yAxis = d3.axisRight(y).ticks(12 * height / width);

  var brush = d3.brush().on("end", brushended),
      idleTimeout,
      idleDelay = 350;

  var tooltip = d3.select("#cluster")
      .append("div")
      .style("position", "absolute")
      .style("z-index", "10")
      .style("visibility", "hidden")
      .style("background", "#ffffff")
      .text("a simple tooltip");

  svg.selectAll("circle")
    .data(points)
    .enter()
    .append("circle")
      .attr("cx", function(d) { return x(d[0]); })
      .attr("cy", function(d) { return y(d[1]); })
      .attr("r", 2.5)
      .attr("fill", function(d) { return z(d[2]); })
      .on("mouseover", function(d){tooltip.text(function(d) { return d[3]; }); return tooltip.style("visibility", "visible");})
      .on("mousemove", function(){return tooltip.style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
      .on("mouseout", function(){return tooltip.style("visibility", "hidden");});

  svg.selectAll(".domain")
      .style("display", "none");

  svg.append("g")
      .attr("class", "brush")
      .call(brush);

  function brushended() {
    var s = d3.event.selection;
    if (!s) {
      if (!idleTimeout) return idleTimeout = setTimeout(idled, idleDelay);
      x.domain(x0);
      y.domain(y0);
    } else {
      x.domain([s[0][0], s[1][0]].map(x.invert, x));
      y.domain([s[1][1], s[0][1]].map(y.invert, y));
      svg.select(".brush").call(brush.move, null);
    }
    zoom();
  }

  function idled() {
    idleTimeout = null;
  }

  function zoom() {
    var t = svg.transition().duration(750);
    svg.select(".axis--x").transition(t).call(xAxis);
    svg.select(".axis--y").transition(t).call(yAxis);
    svg.selectAll("circle").transition(t)
        .attr("cx", function(d) { return x(d[0]); })
        .attr("cy", function(d) { return y(d[1]); });
  }
});

最后,html:

<body>
    <div class="one">
        <div class="two">
          <svg id="cluster" width="700" height="500"></svg>
        </div>
    </div>
</body>

除了工具提示和缩放有点偏离中心之外,一切似乎都工作正常。我很感激任何指导。

最佳答案

这是一个有效的 JS Fiddle:ScatterPlot with zoom and tooltips

主要变化:

  1. 当您将画笔 矩形附加到圆圈顶部时,圆圈上的鼠标事件将永远不起作用。你看,元素的顺序在这里非常重要!所以,这就是我所做的(移动画笔附加代码上方圆圈:

    svg.append("g")
     .attr("class", "brush")
     .call(brush);
    
    svg.selectAll("circle")
    ...
    
  2. 我不确定您为什么要使用 mousemove,因为您只需要当鼠标悬停在圆圈上时才需要工具提示。我将鼠标事件更改为:

    .on("mouseover", function(d){
       tooltip.html(d[3]).style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px").style("visibility", "visible"); 
    }).on("mouseout", function(){return tooltip.style("visibility", "hidden");});
    
  3. 您在 SVG 中有 div 元素(用作工具提示)。我确信这可能是匆忙完成的。这里:我把它移到了 SVG 之外:

    var tooltip = d3.select(".two")
      .append("div")
    
  4. 不建议在设置轴域时使用静态数字。您拥有数据并且知道要从中获取的 xy 点,因此只需使用 d3.extent

    x0 = d3.extent(points, function (d) { return d[0]; }),
    y0 = d3.extent(points, function (d) { return d[1]; })
    

小改动:

  1. 在设置域时使用domain().nice()。当你有负值时它真的很有用。这是doc
  2. 我没有只使用您提供的 2 个点,而是生成了一些随机点并将其用作数据:这也解决了缩放问题(其中我没有进行任何更改,因为您让它工作完美)

    function generateRandomPoints () {
     var points = [];
     for(var i=0; i<10; i++) {
       points.push([Math.random()*30 - 15, Math.random()*20-5, i, 'Cluster '+ (i+1)]);
     }
     return points;
    }
    

这是将上述所有内容组合在一起的片段:

  var data = [
  {
    "points": generateRandomPoints()
  }
];
  
  
  function generateRandomPoints () {
  	var points = [];
    for(var i=0; i<10; i++) {
    	points.push([Math.random()*30 - 15, Math.random()*20-5, i, 'Cluster '+ (i+1)]);
    }
    return points;
  }
  
  var points = data[0].points;

  var svg = d3.select("#cluster"),
      width = +svg.attr("width"),
      height = +svg.attr("height");

  var k = height / width,
      x0 = d3.extent(points, function (d) { return d[0]; }),
      y0 = d3.extent(points, function (d) { return d[1]; }),
      x = d3.scaleLinear().domain(x0).nice().range([0, width]),
      y = d3.scaleLinear().domain(y0).nice().range([height, 0]),
      z = d3.scaleOrdinal(d3.schemeCategory10);
  var xAxis = d3.axisTop(x).ticks(12),
      yAxis = d3.axisRight(y).ticks(12 * height / width);

  var brush = d3.brush().on("end", brushended),
      idleTimeout,
      idleDelay = 350;

  var tooltip = d3.select(".two")
      .append("div")
      .style("position", "absolute")
      .style("z-index", "10")
      .style("visibility", "hidden")
      .style("background", "#ffffff")
      .text("a simple tooltip");

  svg.append("g")
      .attr("class", "brush")
      .call(brush);
      
  svg.selectAll("circle")
    .data(points)
    .enter()
    .append("circle")
      .attr("cx", function(d) { 
      return x(d[0]); 
      })
      .attr("cy", function(d) { 
      return y(d[1]); 
      })
      .attr("r", 4)
      .attr("fill", function(d) { return z(d[2]); })
      .on("mouseover", function(d){
      	tooltip.html(d[3]).style("top", (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px").style("visibility", "visible"); })
      .on("mouseout", function(){return tooltip.style("visibility", "hidden");});

  svg.selectAll(".domain")
      .style("display", "none");

  function brushended() {
    var s = d3.event.selection;
    if (!s) {
      if (!idleTimeout) return idleTimeout = setTimeout(idled, idleDelay);
      x.domain(x0).nice();
      y.domain(y0).nice();
    } else {
      x.domain([s[0][0], s[1][0]].map(x.invert, x));
      y.domain([s[1][1], s[0][1]].map(y.invert, y));
      svg.select(".brush").call(brush.move, null);
    }
    zoom();
  }

  function idled() {
    idleTimeout = null;
  }

  function zoom() {
    var t = svg.transition().duration(750);
    svg.select(".axis--x").transition(t).call(xAxis);
    svg.select(".axis--y").transition(t).call(yAxis);
    svg.selectAll("circle").transition(t)
        .attr("cx", function(d) { return x(d[0]); })
        .attr("cy", function(d) { return y(d[1]); });
  }
<script src="https://d3js.org/d3.v4.min.js"></script>



<div class="one">
  <div class="two">
    <svg id="cluster" width="700" height="500"></svg>
  </div>
</div>

希望这有帮助。

关于javascript - d3.js (v4) 带工具提示的散点图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51456943/

相关文章:

javascript - 谷歌图表自动排序

javascript - 遍历元素的数据属性

javascript - 是否可以将事件监听器绑定(bind)到来自外部脚本的影子 dom 内的元素?

javascript - 加载时 D3 中的转换

javascript - d3.js - 过渡仅在没有 svg 标题的情况下开始,而不是从它开始

带有链接的 jquery UI 工具提示 html

javascript - 将 setInterval 的定时 'click' 更改为 mouseenter/exit

d3.js - d3 滴答只显示星期日 :

银光: "The name already exists in the tree"

tooltip - Stack Overflow 如何显示问题的工具提示?