如何模拟鼠标在D3中移动,所以当你拖动节点,其他节点自动移动? [英] How to simulate mouse move in D3 so when you drag nodes, other nodes move automatically?

查看:158
本文介绍了如何模拟鼠标在D3中移动,所以当你拖动节点,其他节点自动移动?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个粘性布局: http://jsfiddle.net/smqsusdw/

我有一个函数将一个节点拖到一个位置:

I have this function that drags one node to a position :

function positionnodes(){

     force.stop();
     node.each(function(d, i){
         if(i===1){      

         d.fixed = true;
         d.x = 100;
         d.y = 100;
         }
     }).transition().duration(1000).attr("cx", function(d){ return d.x }).attr("cy", function(d){ return d.y });

    link.transition().duration(1000)
                      .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;  });

}

现在当它做到这一点我想它看起来像我我用我的鼠标拖动它。但是当我按下按钮只有选定的节点移动。是否还有模拟节点上的mousedrag,以便其他相关的节点似乎随它移动吗?

Now when it does this I want it to look like I am dragging it with my mouse. But when I press the button only the chosen node moves. Is there anyway to simulate a mousedrag on the node so that the other related nodes seem to move with it ?

例如,我按下按钮,只有一个节点移动,所有其他节点保持放置。

For example, I press the button, only one node moves and all the others stay put.

当我拖动一个节点到一个位置相关节点移动与它由于D3力物理。有没有办法模拟这个运动

But when I drag one of the nodes to a position the related nodes kind of move with it due to the D3 force physics. Is there a way to simulate this movement

推荐答案

为了选择正确的方法,重要的是要知道在D3的力布局中的计算与任何元素的实际渲染去耦合。 d3.layout.force() 将根据指定的参数来计算运动和位置。渲染将由 .force(tick,renderingHandler)注册的处理程序完成。这个函数将被每一个tick的强制布局调用,并根据计算的位置渲染元素。

To choose the right approach it is important to know that in D3's force layout the calculations are decoupled from the actual rendering of any elements. d3.layout.force() will take care of calculating movements and positions according to the specified parameters. The rendering will be done by the handler registered with .force("tick", renderingHandler). This function will get called by the force layout on every tick and render the elements based on the calculated positions.

考虑到这一点,很明显,你的解决方案不会按预期工作。在图形元素上使用转换只会移动节点而不更新数据,并且不会涉及力布局。要获得所需的行为,您需要坚持计算和渲染的解耦。这将免除您实现鼠标事件模拟的需要。

With this in mind it becomes apparent, that your solution will not work as expected. Using transitions on the graphical elements will just move the nodes around without updating the data and without any involvement of the force layout. To get the desired behavior, you need to stick to the decoupling of calculations and rendering. This will free you from the need to implement a simulation of mouse events.

这可以通过使用 d3.timer() ,它会重复调用一个函数将移动节点的位置设置为其开始和结束值。设置这些值之后,函数将激活强制布局以对其余节点执行其工作,并调用呈现处理程序 .tick(),这将更新整个布局。

This could be done by using a d3.timer(), which will repeatedly invoke a function setting the moving node's position to the interpolated values between its start and end values. After having set these values, the function will activate the force layout to do its work for the rest of the nodes and invoke the rendering handler .tick(), which will update the entire layout.

function positionnodes(){

    var move = graph.nodes[1],  // the node to move around
        duration = 1000,        // duration of the movement
        finalPos = { x: 100, y: 100 },
        interpolateX = d3.interpolateNumber(move.x, finalPos.x),
        interpolateY = d3.interpolateNumber(move.y, finalPos.y);

    // We don't want the force layout to mess with our node.
    move.fixed = true;  

    // Move the node by repeatedly determining its position.
    d3.timer(function(elapsed) {

        // Because the node should remain fixed, the previous position (.px, .py)
        // needs to be set to the same value as the new position (.x, .y). This way
        // the node will not have any inherent movement.
        move.x = move.px = interpolateX(elapsed / duration); 
        move.y = move.py = interpolateY(elapsed / duration); 

        // Re-calculate the force layout. This will also invoke tick()
        // which will take care of the rendering.
        force.start();

        // Terminate the timer when the desired duration has elapsed.
        return elapsed >= duration;
    });

}

请查看以下代码段或更新的 JSFiddle 以适应您的代码。

Have a look at the following snippet or the updated JSFiddle for a working adaption of your code.

var graph  ={
  "nodes": [
    {"x": 469, "y": 410},
    {"x": 493, "y": 364},
    {"x": 442, "y": 365},
    {"x": 467, "y": 314},
    {"x": 477, "y": 248},
    {"x": 425, "y": 207},
    {"x": 402, "y": 155},
    {"x": 369, "y": 196},
    {"x": 350, "y": 148},
    {"x": 539, "y": 222},
    {"x": 594, "y": 235},
    {"x": 582, "y": 185},
    {"x": 633, "y": 200}
  ],
  "links": [
    {"source":  0, "target":  1},
    {"source":  1, "target":  2},
    {"source":  2, "target":  0},
    {"source":  1, "target":  3},
    {"source":  3, "target":  2},
    {"source":  3, "target":  4},
    {"source":  4, "target":  5},
    {"source":  5, "target":  6},
    {"source":  5, "target":  7},
    {"source":  6, "target":  7},
    {"source":  6, "target":  8},
    {"source":  7, "target":  8},
    {"source":  9, "target":  4},
    {"source":  9, "target": 11},
    {"source":  9, "target": 10},
    {"source": 10, "target": 11},
    {"source": 11, "target": 12},
    {"source": 12, "target": 10}
  ]
}





var width = 960,
    height = 500;

var force = d3.layout.force()
    .size([width, height])
    .charge(-400)
    .linkDistance(40)
    .on("tick", tick);

var drag = force.drag()
    .on("dragstart", dragstart);

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

var link = svg.selectAll(".link"),
    node = svg.selectAll(".node");

//d3.json("graph.json", function(error, graph) {
 // if (error) throw error;

  force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();

  link = link.data(graph.links)
    .enter().append("line")
      .attr("class", "link");

  node = node.data(graph.nodes)
    .enter().append("circle")
      .attr("class", "node")
      .attr("r", 12)
      .on("dblclick", dblclick)
      .call(drag);
//});

function tick() {
  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 dblclick(d) {
  d3.select(this).classed("fixed", d.fixed = false);
}

function dragstart(d) {
  d3.select(this).classed("fixed", d.fixed = true);
}


function positionnodes(){
    
    var move = graph.nodes[1],  // the node to move around
        duration = 1000,        // duration of the movement
        finalPos = { x: 100, y: 100 },
        interpolateX = d3.interpolateNumber(move.x, finalPos.x),
        interpolateY = d3.interpolateNumber(move.y, finalPos.y);
    
    // We don't want the force layout to mess with our node.
    move.fixed = true;  
    
    // Move the node by repeatedly determining its position.
    d3.timer(function(elapsed) {
        
        // Because the node should remain fixed, the previous position (.px, .py)
        // needs to be set to the same value as the new position (.x, .y). This way
        // the node will not have any inherent movement.
        move.x = move.px = interpolateX(elapsed / duration); 
        move.y = move.py = interpolateY(elapsed / duration); 
        
        // Re-calculate the force layout. This will also invoke tick()
        // which will take care of the rendering.
        force.start();
        
        // Terminate the timer when the desired duration has elapsed.
        return elapsed >= duration;
    });
	
}

.link {
  stroke: #000;
  stroke-width: 1.5px;
}

.node {
  cursor: move;
  fill: #ccc;
  stroke: #000;
  stroke-width: 1.5px;
}

.node.fixed {
   fill: #f00;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<button onclick = 'positionnodes()'> click me</button>

这篇关于如何模拟鼠标在D3中移动,所以当你拖动节点,其他节点自动移动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆