d3js 为什么我的固定节点在移动?

d3js why are my fixed nodes moving?

我的问题如下:虽然我为我的节点设置了 fixed : true,但只有第一个(从 json 文件加载)是固定的。新的(用户创建的)不是固定的,即使用浏览器的开发者工具,我们也能看到fixed="true"属性。 redraw() 函数中是否缺少某些内容?我应该使用 tick() 吗?我在这个问题上奋斗了一个星期,我再也不知道了。

这是我的全部代码(其中初始节点确实是固定的),但我还提出了一个 jsfiddle(其中初始节点也不固定)。

// Plan
var width = 960,
    height = 500;

// Orange selected element
var selected_node = null,
    selected_link = null;

// Element we clicked on it
var mouseclick_node = null,
    mouseclick_link = null;

// To detect click or double click.
var clickedOnce = false,
    timer;

// Useful function to compute the size of an element.
d3.selection.prototype.size = function() {
  var n = 0;
  this.each(function() { ++n; });
  return n;
};


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

var visual = svg
   .append('svg:g')
   .append('svg:g')
      .on("mousemove", mousemove)
      .on("click", click);

// ajoute une balise rect nested dans g, ave préfix svg
visual.append('svg:rect')
    .attr('width', width)
    .attr('height', height)
    .attr('fill', 'white');

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

// Future link
var drag_line = visual.append("line")
    .attr("class", "drag_line")
    .attr("x1", 0)
    .attr("y1", 0)
    .attr("x2", 0)
    .attr("y2", 0);

// get layout properties
var nodes = force.nodes(),
    links = force.links();
var node = visual.selectAll(".node"),
    link = visual.selectAll(".link");

// Allows the drag actions 
var drag = force.drag();

// Read the json file and creates the links and the nodes
  d3.json("graph_empty.json", function(error, graph) {

    if (error) console.log("error: " + error);   

    nodes = graph.nodes;
    links = graph.links;

    // To redraw after loading nodes and links
    redraw();
});


// Add properties to links and nodes
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 mousemove() {
  var point = d3.mouse(this), p = {x: point[0], y: point[1]};

  if (selected_node) {
    drag_line
      .attr("x1", selected_node.x)
      .attr("y1", selected_node.y)
      .attr("x2", p.x)
      .attr("y2", p.y);
  }
}

function click() { 
  var point = d3.mouse(this), p = {x: point[0], y: point[1]};

  // if already clicked once
  if (clickedOnce) {
      run_on_double_click();
  }
  // otherwise
  else {
    timer = setTimeout(function() {
       run_on_simple_click(point);
    }, 150);
    clickedOnce = true;
  }
}

function run_on_simple_click(p) {
  console.log("[run_on_simple_click]");
  clickedOnce = false;

  if (!mouseclick_node && selected_node) {

    // add a new node
    point = {x: p[0], y: p[1]};   
    nodes.push(point);

    // add link to mousedown node
    links.push({source: selected_node, target: point});

    // select the new node
    selected_node = point;
    selected_link = null;

    // update the drag line
    drag_line
      .attr("x1", selected_node.x)
      .attr("y1", selected_node.y)
      .attr("x2", p.x)
      .attr("y2", p.y);

    redraw();
  } 
}

function run_on_double_click() {
    clickedOnce = false;
    clearTimeout(timer);
    console.log("[run_on_double_click]");
}

function resetMouseVars() {
  console.log("[resetMouseVars]");
  mouseclick_node = null;
  mouseclick_link = null;
}

function redraw() {
  console.log("[redraw start]");

  force
      .nodes(nodes)
      .links(links)

  link = link.data(links);

  link.enter().append("line")
    .attr("class", "link")
    .on("click", function(d) {
        // double click:
        if (clickedOnce_On) {
            clickedOnce_On = false;
            clearTimeout(timer);

            mouseclick_link = d;
            if (!selected_link)
              selected_link = mouseclick_link;
            else if (mouseclick_link == selected_link) 
              selected_link = null;
            else if (mouseclick_link == selected_link) {
              selected_link = null; 
              selected_link = mouseclick_link;
            } 
            selected_node = null; 
            resetMouseVars();
            redraw(); 
        }
        // single click:
        else {
          timer = setTimeout(function() {
             // to do... create a new node between !
          }, 250);
          clickedOnce_On = true;
        }
    });

  link.exit().remove();

  link.classed("link_selected", function(d) { 
                  return d === selected_link; 
              });

  node = node.data(nodes);

  var clickedOnce_On = false;
  node.enter().append("circle")
    .attr("class", "node")
    .attr("r", 6)
    .attr("fixed", true)
    .call(drag)
    .on("click", function(d) {
        mouseclick_node = d;

        // double click:
        if (clickedOnce_On) {
            clickedOnce_On = false;
            clearTimeout(timer);

            if (!selected_node)
                selected_node = mouseclick_node;
            else if (mouseclick_node == selected_node) 
              selected_node = null;
            else if (mouseclick_node == selected_node) {
              selected_node = null; 
              selected_node = mouseclick_node;
            } 
            selected_link = null; 
            resetMouseVars();
            redraw(); 
        }
        // single click:
        else {
          timer = setTimeout(function() {
            // close the surface if
             if ((selected_node) && (mouseclick_node) && (selected_node !=  mouseclick_node)) {
                console.log("mouseclick_node: "+ mouseclick_node);
                // Here: count the number of links for each node
                // and find the node with only one link.
                // If this node == mouseclick_node,
                // we add a new link between it and the selected_node.
                resetMouseVars();
                redraw(); 
             }
          }, 250);
          clickedOnce_On = true;
        }
    });

  node.exit().transition()
    .attr("r", 0)
    .remove();

  node.classed("node_selected", function(d) { 
                  return d === selected_node; 
              }); 
  force
      .start()
      .tick();

  console.log("[redraw end]");
}

非常感谢您的任何 suggestions/comments/solutions !

最后我找到了一个解决方案:我这样设置 force 的属性:

var force = d3.layout.force()
    .size([width, height])
    .charge(0)
    .gravity(0)
    .linkStrength(0)
    .friction(0)
    .on("tick", tick);

这样,我所有的固定节点都不会移动,除非我拖动它们。