【问题标题】:D3 v4 force layout disable animation on dragD3 v4 强制布局在拖动时禁用动画
【发布时间】:2018-01-02 10:29:55
【问题描述】:

如何在d3版本4的力模拟中拖动当前节点时禁用动画

下面是用于拖动节点的代码

var node = svg.selectAll(".node")
            .data(json.nodes)
            .enter().append("g")
            .attr("class", "node")
            .call(d3.drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended));

      function dragstarted(d) {
        if (!d3.event.active) force.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) force.alphaTarget(0.3);
        // d.fx = null;
        //d.fy = null;
    } 

当我尝试在拖动方法中停止强制时,它不起作用。 请建议我停止动画的最佳方法

请查看fiddle

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    “禁用动画” 的意思并不完全清楚。我想你是在谈论其他节点的移动......嗯,这是完全预期的行为,因为你正在重新加热模拟。

    一个可能的解决方案是在模拟结束时设置所有节点的fxfy 属性:

    simulation.on("end", function() {
        node.each(function(d) {
            d.fx = d.x;
            d.fy = d.y;
        })
    })
    

    这是您进行更改的代码。等待直到模拟结束(大约 5 秒)然后拖动节点:

    var nodes = [{
      "id": 1,
      "name": "server 1"
    }, {
      "id": 2,
      "name": "server 2"
    }, {
      "id": 3,
      "name": "server 3"
    }, {
      "id": 4,
      "name": "server 4"
    }, {
      "id": 5,
      "name": "server 5"
    }, {
      "id": 6,
      "name": "server 6"
    }, {
      "id": 7,
      "name": "server 7"
    }, {
      "id": 8,
      "name": "server 8"
    }, {
      "id": 9,
      "name": "server 9"
    }]
    
    var links = [{
      source: 1,
      target: 2
    }, {
      source: 1,
      target: 3
    }, {
      source: 1,
      target: 4
    }, {
      source: 2,
      target: 5
    }, {
      source: 2,
      target: 6
    }, {
      source: 3,
      target: 7
    }, {
      source: 5,
      target: 8
    }, {
      source: 6,
      target: 9
    }, ]
    
    var index = 10;
    var svg = d3.select("svg"),
      width = +svg.attr("width"),
      height = +svg.attr("height"),
      node,
      link;
    
    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));
    
    update();
    
    function update() {
      link = svg.selectAll(".link")
        .data(links, function(d) {
          return d.target.id;
        })
    
      link = link.enter()
        .append("line")
        .attr("class", "link");
    
      node = svg.selectAll(".node")
        .data(nodes, function(d) {
          return d.id;
        })
    
      node = node.enter()
        .append("g")
        .attr("class", "node")
        .on("click", click)
        .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));
    
      node.append("circle")
        .attr("r", 2.5)
    
      node.append("title")
        .text(function(d) {
          return d.id;
        });
    
      node.append("text")
        .attr("dy", 3)
        .text(function(d) {
          return d.name;
        });
    
      simulation
        .nodes(nodes)
        .on("tick", ticked)
        .on("end", function() {
          node.each(function(d) {
            d.fx = d.x;
            d.fy = d.y;
          })
        })
    
      simulation.force("link")
        .links(links);
    }
    
    function click(d) {
      nodes.push({
        id: index,
        name: "server " + index
      });
      links.push({
        source: d.id,
        target: index
      });
      index++;
      update();
    }
    
    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("transform", function(d) {
          return "translate(" + d.x + ", " + d.y + ")";
        });
    }
    
    function dragstarted(d) {
      if (!d3.event.active) simulation.alphaTarget(0.3).restart()
    }
    
    function dragged(d) {
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    }
    
    function dragended(d) {
      if (!d3.event.active) simulation.alphaTarget(0);
    }
    .link {
      stroke: #aaa;
    }
    
    .node {
      pointer-events: all;
      stroke: none;
      stroke-width: 40px;
      cursor: pointer;
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <svg width="600" height="400"></svg>

    【讨论】:

    • 如何修复节点,无需等待 5 秒
    • 没听懂,能改一下吗?
    • 似乎在 d3 中我们使用 node.fixed = true。在 d3 v4 中固定节点或等效于 fixed = true 的最佳方法是什么
    • 这是d.fxd.fy。您在代码中使用它们。
    【解决方案2】:

    看看这个例子:http://bl.ocks.org/norrs/2883411,因为它似乎完成了你所追求的。

    如示例和相关的 SO 问题 (D3 force directed graph with drag and drop support to make selected node position fixed when dropped) 中所示,您最好创建和使用自己的拖动侦听器来实现此特定行为。

    【讨论】:

      【解决方案3】:

      其实很简单。根本不定义:

      .call(d3.drag().on("start", drag_start)
                       .on("drag", drag_drag)
                       .on("end", drag_end))
      

      这是控制动画部分的原因,但是由于数据量大或其他原因,您在图形延迟加载时发现问题,然后我建议添加一个名为 .alphaDecay(0.9) 的属性,这个特殊的东西会减少链接长度从而减少图表的加载时间。

      【讨论】:

        猜你喜欢
        • 2015-05-27
        • 2017-08-30
        • 1970-01-01
        • 2023-03-19
        • 2014-01-15
        • 2014-08-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多