【问题标题】:How to disable animation in a force-directed graph? [closed]如何在力导向图中禁用动画? [关闭]
【发布时间】:2018-05-10 16:29:18
【问题描述】:

有没有办法在 D3 力导向图中禁用动画?

我正在处理这个例子:https://bl.ocks.org/mbostock/4062045

我想在没有初始动画的情况下渲染图形,即显示所有节点和链接的最终位置。

【问题讨论】:

  • 我想知道为什么它被关闭为“为什么这段代码不起作用?”。这显然不是这里的情况。充其量,这将是“太宽泛”。即便如此,两位 D3 的金徽章用户在这里发表评论认为没有理由关闭它。我刚刚投票决定重新开放。
  • 我投票决定重新提出这个问题,再次...正如我在上面的评论中所说,目前尚不清楚为什么模组将其关闭为题外话。
  • @altocumulus 我主要不同意原因:“为什么这段代码不起作用?”。这显然不是这里的情况。 太宽泛可能会好一点,但即便如此,你比我更清楚这种问题在 D3 社区中相当普遍:OP 共享代码并要求在某些 D3 方面进行修改其中。我不相信 S.O. 有那么低。标准。
  • @GerardoFurtado 同意,这就是我重新投票的原因。我支持你的评估,即如果有的话,最接近的原因应该太宽泛。有这么多质量最低的问题肯定值得被抨击,我主张严格投票、近距离投票和删除这些帖子以保持高质量。我认为这个问题的主要问题是你必须是 D3 领域的专家才能意识到它实际上是重点、简洁且值得彻底回答。
  • @meagar 这行得通吗?我真的可以 ping 接近的选民吗?您能否重新考虑您决定通过 cmets 阅读该问题并给出对问题和答案的赞成票?由于d3 是一个相当狭窄的标签,我怀疑我们是否会获得足够多的选民来以常规方式重新打开它。

标签: javascript d3.js force-layout d3-force-directed


【解决方案1】:

尽管这个问题已经有一个accepted answer,但建议的解决方案不是在 D3 力图中禁用动画的正确方法。浏览器仍在移动节点和链接在每个滴答声!您只是没有看到它们移动,但浏览器正在移动它们,进行大量计算并浪费大量时间/资源。此外,您不需要服务器端。

我的回答提出了一个不同的解决方案,实际上并不绘制动画。例如,您可以在 Mike Bostock(D3 创建者)的 this code 中看到它。

当您了解tick 函数是什么时,这个解决方案很容易理解:它只是一个计算模拟中所有位置并前进一步的函数。尽管绝大多数 D3 力导向图在每个刻度处绘制节点和链接,但您不需要这样做。

你可以这样做:

  1. 在定义后立即使用stop() 停止模拟:

    var simulation = d3.forceSimulation(graph.nodes)
        .force("link", d3.forceLink().id(function(d) { return d.id; }))
        .force("charge", d3.forceManyBody())
        .force("center", d3.forceCenter(width / 2, height / 2))
        .stop();//stop the simulation here
    
  2. 让模拟运行不绘制任何东西。这是最重要的一步:您不必在每个刻度处移动元素。在这里,我正在运行 300 个滴答声,这大约是默认数字:

    for (var i = 0; i < 300; ++i) simulation.tick();
    
  3. 然后,只需使用模拟创建的属性(xysourcetarget)来绘制圆圈和线条,只需一次:: p>

    node.attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; })
    

以下是仅包含这些更改的链接块:http://bl.ocks.org/anonymous/8a4e4e2fed281ea5e2a5c804a9a03783/85ced3ea82a4bed20a2010530562b655d8f6e464

比较这个解决方案的时间与“隐藏节点”解决方案的时间(接受的答案)。这里的这个速度更快。在我的测试中,我得到了这个结果:

  • “隐藏节点”解决方案:大约 5000 毫秒
  • 此解决方案:大约 200 毫秒

也就是说,速度提高了 25 倍。

PS:为简单起见,我删除了分叉块中的 ticked 函数。如果要拖动节点,只需将其添加回来即可。


为 D3 v5.8 编辑

现在 D3 v5.8 允许将交互次数传递给 simulation.tick(),您甚至不再需要 for 循环。所以,而不是:

for (var i = 0; i < 300; ++i) simulation.tick();

你可以这样做:

simulation.tick(300);

【讨论】:

  • 我很乐意删除我的答案,但我不确定是否可以。也许我可以举报它?
  • @John 我认为你不应该删除你的答案:它有信息,我们永远不应该删除信息。但是,您有几个反对票(您看不到它,但您有 1 个赞成票和 2 个反对票)......所以,您可以编辑它,说明您只是隐藏了模拟。这样你就可以让 OP 清楚用户没有看到动画,但动画正在正在运行。
  • 感谢您的建议!希望这能引导人们找到更好的答案。
  • @GerardoFurtado 您确定指向 Mike Bostock 示例的链接(仍然)正确吗?在他的代码中,我看不出与您答案中的代码有任何相似之处。
  • @GerardoFurtado 我们如何在仍然允许单击和拖动的情况下创建相同的图表?只是没有重力/物理学?
【解决方案2】:

编辑

这个方法只是隐藏了模拟的动画部分。请参阅Gerardo Furtado's answer,它在不绘制中间结果的情况下执行模拟,这意味着用户无需等待解决方案正在缓慢发展。

========

“动画”实际上是模拟运行。可以使用模拟运行的时间,但这可能意味着节点会卡在局部最小值 - see the documentation here for more details

您可以选择为模拟完成时触发的end 事件添加一个侦听器。我创建了一个 sn-p,其中 Graph 最初是隐藏的,然后在完成模拟后出现。

另一种方法是在服务器端渲染图表(如果这是一个选项),然后提供一个现成的 SVG,可以使用 d3 进一步操作。

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

var color = d3.scaleOrdinal(d3.schemeCategory20);

var simulation = d3.forceSimulation()
  .force("link", d3.forceLink().id(function(d) {
    return d.id;
  }))
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2))
  .on('end', function() {
    svg.classed('hidden', false)
    d3.select('#loading').remove()
  });

// I wasn't able to get the snippet to load the original data from https://bl.ocks.org/mbostock/raw/4062045/miserables.json so this is a copy hosted on glitch
d3.json("https://cdn.glitch.com/8e57a936-9a34-4e95-a03d-598e5738f44d%2Fmiserables.json", function(error, graph) {
  if (error) {
    console.log(error)
  };

  var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("line")
    .attr("stroke-width", function(d) {
      return Math.sqrt(d.value);
    });

  var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr("fill", function(d) {
      return color(d.group);
    })
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended));

  node.append("title")
    .text(function(d) {
      return d.id;
    });

  simulation
    .nodes(graph.nodes)
    .on("tick", ticked);

  simulation.force("link")
    .links(graph.links);

  function ticked() {
    link
      .attr("x1", function(d) {
        return d.source.x;
      })
      .attr("y1", function(d) {
        return d.source.y;
      })
      .attr("x2", function(d) {
        return d.target.x;
      })
      .attr("y2", function(d) {
        return d.target.y;
      });

    node
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      });
  }
});

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}
.links line {
  stroke: #999;
  stroke-opacity: 0.6;
}

.nodes circle {
  stroke: #fff;
  stroke-width: 1.5px;
}

.hidden {
  visibility: hidden
}

img {
    display: block;
    margin-left: auto;
    margin-right: auto;
   }
<script src="https://d3js.org/d3.v4.min.js"></script>
<img id ="loading" src="http://thinkfuture.com/wp-content/uploads/2013/10/loading_spinner.gif" />
<svg width="960" height="600" class="hidden"></svg>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-01
    • 2021-09-12
    • 2015-08-04
    • 2013-02-17
    • 1970-01-01
    相关资源
    最近更新 更多