【问题标题】:Different Tooltip for each SVG circle on D3 MapD3 Map 上每个 SVG 圆圈的不同工具提示
【发布时间】:2019-02-22 08:29:13
【问题描述】:

我正在制作与其绩效评级相关的企业世界地图:因此每个企业都将由一个点表示,该点有一个带有绩效(和其他信息)的工具提示。我正在使用地图示例 here

地图数据:

pointData = {
    "businessName": businessName,
    "location": location,
    "performance": currperformance
}

pointsData.push(pointData);

因此 pointsData JSON 对象的形式为 [{"business":"b1","location":[long1, lat1]},{"businessName":"b2","location":[long2, lat2]}...]

工具提示问题:

我可以完美地显示带有点和相同工具提示的地图,直到我尝试使用不同的工具提示。我使用动态工具提示研究过的许多 D3 示例仅适用于图表 - 我的困难是在地图上的每个 SVG 圆圈上附加工具提示的 JSON 数据。

这是我迄今为止的尝试,它显示 no points 并且没有显示控制台错误(添加 .each(function (d, i) {..}doesn't 不再绘制部件,但有必要将每个位置链接到它的后续业务和绩效评级。)

d3.json("https://raw.githubusercontent.com/d3/d3.github.com/master/world-110m.v1.json", function (error, topo) {
    if (error) throw error;

    gBackground.append("g")
        .attr("id", "country")
        .selectAll("path")
        .data(topojson.feature(topo, topo.objects.countries).features)
        .enter().append("path")
        .attr("d", path);


    gBackground.append("path")
        .datum(topojson.mesh(topo, topo.objects.countries, function (a, b) { return a !== b; }))
        .attr("id", "country-borders")
        .attr("d", path);


    //Tooltip Implementation
    var tooltip = d3.select("body").append("div")
        .attr("class", "tooltip")
        .style('opacity', 0)
        .style('position', 'absolute')
        .style('padding', '0 10px');


    gPoints.selectAll("circle")
        .each(function (d, i) {
            this.data(pointsData.location).enter()
                .append("circle")
                .attr("cx", function (d, i) { return projection(d)[0]; })
                .attr("cy", function (d, i) { return projection(d)[1]; })
                .attr("r", "10px")
                .style("fill", "yellow")
                .on('mouseover', function (d) {
                    tooltip.transition()
                        .style('opacity', .9)
                        .style('background', 'black')
                        .text("Business" + pointsData.businessName + "performance" + pointsData.performance)
                        .style('left', (d3.event.pageX - 35) + 'px')
                        .style('top', (d3.event.pageY - 30) + 'px')
                })
                .on('mouseout', function (d) {
                    tooltip.transition()
                        .style("visibility", "hidden");
                })
        });
}); 

【问题讨论】:

  • 你不需要each。只需使用常规输入选择。
  • @GerardoFurtado 是的.data(points).enter() 确实在我拥有所有地图纬度和经度的数组时绘制了所有点 - 但我试图循环通过 pointsData 对象,因为链接 each 纬度/经度圈到它的对应数据,以便每个工具提示可以不同
  • 每个工具提示中的数据会有所不同,只需简单、常规的回车选择即可。您是否在“鼠标悬停”回调中看到您从未使用过的 d 参数?使用它。
  • 我不确定,但是您似乎误解了d3的数据连接原理。 d 将是每个数据点在 3 种状态之一的迭代(输入、删除、更新)bost.ocks.org/mike/join

标签: javascript d3.js svg tooltip


【解决方案1】:

回车选择可以在不使用.each() 的情况下完成您尝试执行的操作。 Bostock 设计 D3 以将数据连接到元素,以便:

与其告诉 D3 如何做某事,不如告诉 D3 你想要什么。你 希望圆圈元素与数据相对应。你想要一个圈子 每个数据。与其指示 D3 创建圆圈,不如告诉 D3 选择“圆圈”应与数据相对应。这个概念是 称为数据连接 (Thinking with Joins)。

我建议您看一些有关进入、更新和退出选择的示例。不过,您最初可能是使用普通圆圈(和相同的工具提示)执行此操作的:

  svg.selectAll("circle")
      .data([[long,lat],[long,lat]])
      .enter()
      .append("circle")
      .attr("cx", function(d,i) { return projection(d)[0]; })
      .attr("cy", function(d,i) { return projection(d)[1]; })

如果您是,那么只需访问数据以获取其他属性。如果不是,那么这是正确使用输入选择的问题。 无论如何,这是一个使用输入选择和您指定的数据格式的可能实现:

var pointsData = [
  { "businessName": "Business A",
    "location": [50,100],
    "performance": "100" },
  { "businessName": "Business B",
    "location": [100,50],
    "performance": "75"
  },  
  { "businessName": "Business C",
    "location": [150,150],
    "performance": "50"
  }];
  
var svg = d3.select("body")
  .append("svg")
  .attr("width",300)
  .attr("height",300);
  
var tooltip = d3.select("body").append("div")
  .attr("class", "tooltip")
  .style('opacity', 0)
  .style('position', 'absolute')
  .style('padding', '0 10px');
  
svg.selectAll("circle") // empty selection
  .data(pointsData)     // data to bind to selection
  .enter()              // the enter selection holds new elements in the selection
  .append("circle")     // append a circle for each new element
    // Manage the style of each circle based on its datum:
    .attr("cx",function(d) { return d.location[0]; })
    .attr("cy",function(d) { return d.location[1]; })
    .attr("r",10)
    .on("mouseover", function(d) {
      tooltip.transition()
        .style('opacity', .9)
        .style('background', 'steelblue')
        .text("Business: " + d.businessName + ". performance " + d.performance)
        .style('left', (d3.event.pageX - 35) + 'px')
        .style('top', (d3.event.pageY - 30) + 'px')
        .duration(100);
    })
    .on("mouseout",function(d) {
       tooltip.transition()
         .style("opacity", "0")
         .duration(50);
    })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>

原来的选择svg.selectAll("circle") 是空的。将数据绑定到此空选择时,enter 选择将为数据数组中的每个项目保留一个项目,该项目在选择中没有相应的元素(在本例中为一个圆圈,并且由于没有,因此输入数组对于数据数组中的每一项都有一项)。然后,我们为输入选择中的每个项目附加一个元素,并根据每个数据的属性对其进行样式化。

请注意,这需要对您的原始代码进行一些修改(我还跳过了一个投影以使其更简洁的 sn-p)。

  1. 访问基准的属性:

我想您的初始数据集如下所示:[[long,lat], [long,lat], [long,lat]]。访问此数据集中的每个数据时,您可以将圆居中,如下所示:

  .attr("cx", function(d,i) { return projection(d)[0]; })
  .attr("cy", function(d,i) { return projection(d)[1]; })

这些是您在上面的示例中使用的行。但是,您的数据现在看起来像示例代码中的变量 pointData,因此您需要将其修改为:

.attr("cx", function(d,i) { return projection(d.location)[0]; })
.attr("cy", function(d,i) { return projection(d.location)[1]; })

我还访问了工具提示文本的每个数据的适当属性,而不是访问未绑定到每个元素的数据(即使它来自同一来源)。

  1. 不透明度与可见性:

您最初将工具提示的不透明度设置为零,然后在鼠标悬停时将其修改为 0.9:.style('opacity', .9),而不是切换可见性(只有 mouseout 函数可以)在鼠标悬停时将不透明度改回零。

【讨论】:

  • 谢谢一百万 - 我对 JS 的经验很少(第一次使用 D3)所以这消除了我对数据绑定的困惑!
  • 还有1个问题:如何链接到我已有的地图缩放功能? 直到数据点不显示我缩放:function zoomed() { projection.translate(d3.event.translate).scale(d3.event.scale); gBackground.selectAll("path").attr("d", path) gPoints.selectAll("circle") .attr("cx", function (d, i) { return projection(d.location)[0]; }) .attr("cy", function (d, i) { return projection(d.location)[1]; }) }
  • 很难说,你用的是v4吗?作为输入,更新周期有点不同,可以破坏 v3 的代码。我会把它作为一个新问题发布,如果可能的话,还包括执行初始选择和附加的代码(作为一个新问题,你也会得到更多的关注 - cmets 的知名度不高)
猜你喜欢
  • 2013-12-19
  • 1970-01-01
  • 2017-01-08
  • 1970-01-01
  • 2017-05-29
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多