【问题标题】:D3 .merge functionD3 .merge 函数
【发布时间】:2018-04-14 11:33:43
【问题描述】:

尽管阅读了无数次 D3 API,但我仍在努力理解 D3 中的合并功能。

API 说:“此方法通常用于在数据连接后合并输入和更新选择。分别修改输入和更新元素后,您可以合并两个选择并在两者上执行操作,而无需重复代码。 "

这是一个在力导向图表中被认为是直接使用它的示例,其中每个刻度都会调用刻度函数:

var simulation = d3.forceSimulation(nodes)
    .force("charge", chargeForce)
    .force("center", centerForce)
    .on("tick", ticked);

    function ticked() {

    var u = d3.select("svg").selectAll("circle").data(nodes)

    u.enter().append("circle").attr("r",5)
        .merge(u) // What is the merge function doing here?
        .attr("cx", d => d.x)
        .attr("cy", d => d.y)

    u.exit().remove() // Why is it necessary to remove excess objects w/ the exit selection?

    }

我了解数据绑定的工作原理,以及 enter() 和 exit() 选择的工作原理。但是,我以前从未使用过“合并”,我不明白它在这里做什么。如果有人可以逐步简要介绍此功能中发生的事情,那将非常有用。我相信其他人也有类似的问题。

【问题讨论】:

  • 你从哪里得到这个例子的?您应该在 tick 函数中添加元素或重新绑定数据。
  • 很难理解的是,您有 2 个选择并将它们组合成 1 个选择。就像你有 2 个数组,A 和 B。你可以将它们连接成数组 C。
  • @EricGuan 你说的不正确:merge() 不连接选择。 API 明确声明:“此方法不适用于连接任意选择,但是:如果此选择和指定的其他选择在同一索引处都有(非空)元素,此选择的元素在合并中返回,而另一个选择的元素被忽略。".
  • @GerardoFurtado 我在这里找到了:d3indepth.com/force-layout,为什么这样做没有意义?
  • @HarryCramer 这很容易理解:为什么要重新绑定数据并每秒计算数十次进入和退出选择,如果数据没有改变乙>?这没有道理!我刚刚用替代代码写了一个解释它的答案。

标签: javascript d3.js data-visualization


【解决方案1】:

文档很好地解释了该函数的作用,因此它的作用是您不必这样做

u.attr("cx", d => d.x)
 .attr("cy", d => d.y);

u.enter().append("circle").attr("r",5)
        .attr("cx", d => d.x)
        .attr("cy", d => d.y);

您只需拨打attr 一次即可

u.enter().append("circle").attr("r",5)
        .merge(u) // after this point, any updates will apply to both u and u.enter() selections
        .attr("cx", d => d.x)
        .attr("cy", d => d.y)

它将在u-更新选择和u.enter()-输入选择上设置属性cxcy

为什么有必要通过退出选择删除多余的对象?

因为退出选择包含任何额外的 DOM 元素,这些元素未绑定到您传递给 data() 的数组中的元素,所以您可以对退出集合执行任何您需要的操作,例如通过调用 u.exit().style(...) 设置样式等,而不是调用 remove 从 DOM 中删除它们

【讨论】:

    【解决方案2】:

    这里实际上有两个问题:

    1. 了解merge() 方法;
    2. 了解您共享的那段代码;

    关于#1,您已经收到了答复。关于#2,这是我的两分钱:该代码确实有意义。

    这很容易理解:ticked 函数每秒运行几十次。 如果数据没有改变,你为什么要重新绑定数据并重新分配更新、进入和退出选择每秒数十次? (值得一提的是,那段代码的作者是个不错的程序员,这里发生了一些奇怪的事情……毕竟我们都会犯错误)

    ticked 函数只需要计算元素的位置即可。

    这是您与ticked 函数链接的相同代码,简化为:

    function ticked() {
        u.attr('cx', function(d) {
                return d.x;
            })
            .attr('cy', function(d) {
                return d.y;
            })
    }
    

    这里是运行代码:

    var width = 600,
      height = 400;
    
    var colorScale = ['orange', 'lightblue', '#B19CD9'];
    var xCenter = [100, 300, 500]
    
    var numNodes = 100;
    var nodes = d3.range(numNodes).map(function(d, i) {
      return {
        radius: Math.random() * 25,
        category: i % 3
      }
    });
    
    var u = d3.select('svg g')
      .selectAll('circle')
      .data(nodes);
    
    var enter = u.enter()
      .append('circle')
      .attr('r', function(d) {
        return d.radius;
      })
      .style('fill', function(d) {
        return colorScale[d.category];
      });
    
    u = enter.merge(u);
    
    u.exit().remove();
    
    var simulation = d3.forceSimulation(nodes)
      .force('charge', d3.forceManyBody().strength(5))
      .force('x', d3.forceX().x(function(d) {
        return xCenter[d.category];
      }))
      .force('collision', d3.forceCollide().radius(function(d) {
        return d.radius;
      }))
      .on('tick', ticked);
    
    function ticked() {
      u.attr('cx', function(d) {
          return d.x;
        })
        .attr('cy', function(d) {
          return d.y;
        })
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <div id="content">
      <svg width="700" height="400">
        <g transform="translate(50, 200)"></g>
      </svg>
    </div>

    【讨论】:

    • 谢谢 Gerardo,这很有帮助。我很困惑为什么需要重新选择,所以你的澄清很好。当你遇到这样的事情时,你永远不会知道自学。
    • 不用担心。您可以随时向该代码的作者发送电子邮件,询问他为什么这样做。正如我所说,这对我来说没有多大意义。
    • "勾选的功能只需要设置位置" - 谢谢!
    【解决方案3】:

    鉴于 cmets,我应该提醒读者这个答案可能是错误的。所以请先阅读cmets

    TL;DR - 合并将两个节点集合合二为一

    var x = d3.selectAll(".node");
    var y = d3.selectAll(".link");
    var z = x.merge(y);
    

    z 现在包含 x 中的所有元素和 y 中的所有元素。

    【讨论】:

    • 那不正确。 x 和 y 将共享索引,因此您最终只会得到两个选择之一。准确地说 z 将只是 x 选择
    • @40detectives 我在发布之前测试了这段代码,效果很好
    • 我看到@40detectives 描述的行为。
    • 对不起,这个答案肯定是错误的,merge() 是为了结合selectAll()enter() 的结果,从第一个开始填写null 条目。要获得组合选择,您可以:selectAll('.node,.link').
    猜你喜欢
    • 2023-02-21
    • 1970-01-01
    • 1970-01-01
    • 2020-08-25
    • 1970-01-01
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    • 2014-08-09
    相关资源
    最近更新 更多