如何更新d3力元素?

罗杰

d3-force单击时我想更新元素,但是发现错误。我想要的是:

  1. 首先生成所有链接
  2. 然后生成所有节点,以便该节点可以覆盖链接

因此,单击时元素应为:

<svg>
  <line ...></line>
  <line ...></line>
  <line ...></line>
  <line ...></line>
  <g><circle ...></circle></g>
  <g><circle ...></circle></g>
  <g><circle ...></circle></g>
  <g><circle ...></circle></g>
  <g><circle ...></circle></g>
</svg>

但是我得到:

<svg>
  <g><circle ...></circle></g>
  <line ...></line>
  <line ...></line>
  <line ...></line>
  <line ...></line>
  <g><circle ...></circle></g>
  <g><circle ...></circle></g>
  <g><circle ...></circle></g>
  <g><circle ...></circle></g>
</svg>

const width = 800;
const height = 400;

const svg = d3.select("svg").attr("viewBox", [0, 0, width, height]);
let node = svg.selectAll(".node");
let link = svg.selectAll(".link");

const simulation = d3
  .forceSimulation()
  .force("center", d3.forceCenter(width / 2, height / 2))
  .on("tick", (d) => {
    link
      .attr("x1", d => d.source.x)
      .attr("y1", d => d.source.y)
      .attr("x2", d => d.target.x)
      .attr("y2", d => d.target.y);

    node
      .attr("transform", d => `translate(${d.x}, ${d.y})`);
  });

const root = {
  x: width / 2,
  y: height / 2,
  level: 0,
  id: "1"
};

function genChildren(p) {
  const dist = 100 / (p.level + 1);
  const nodes = [{
      x: p.x - dist,
      y: p.y,
      level: p.level + 1,
      id: p.id + "1"
    },
    {
      x: p.x + dist,
      y: p.y,
      level: p.level + 1,
      id: p.id + "2"
    },
    {
      x: p.x,
      y: p.y - dist,
      level: p.level + 1,
      id: p.id + "3"
    },
    {
      x: p.x,
      y: p.y + dist,
      level: p.level + 1,
      id: p.id + "4"
    },
  ];
  const links = nodes.map(v => ({
    source: p.id,
    target: v.id
  }));
  return {
    nodes,
    links
  };
}


function update(nodes, links) {
  link = link.data(links)
    .join(enter => {
      return enter.append("line")
        .attr("stroke", "#000")
        .attr("stroke-width", "1.5px")
        .call(enter => enter.transition().attr("stroke-opacity", 1));
    }, update => update, exit => exit.remove());
  node = node.data(nodes, d => d.id)
    .join(
      enter => {
        const g = enter.append("g");
        g.append("circle")
          .attr("r", 12)
          .attr("cursor", "move")
          .attr("fill", "#ccc")
          .attr("stroke", "#000")
          .attr("stroke-width", "1.5px");
        return g;
      },
      update => update,
      exit => exit.remove(),
    )
    .attr("transform", function(d) {
      return "translate(" + d.x + ", " + d.y + ")";
    })
    .on("click", (ev, d) => {
      if (d.level !== 0) {
        return;
      }
      const childrenInfo = genChildren(root)
      const nodes = d.active ? [root] : [root].concat(childrenInfo.nodes);
      const links = d.active ? [] : childrenInfo.links;
      d.active = !d.active;
      update(nodes, links);
    });

  simulation.nodes(nodes).force("link", d3.forceLink(links).id(node => node.id).strength(-0.01));

  if (simulation.alpha() <= 1) {
    simulation.alpha(1);
    simulation.restart();
  }
}

update([root], [])
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>

鲁本·赫斯鲁特(Ruben Helsloot)

您可以g为节点和链接使用单独的元素,将链接添加到第一个,然后将节点添加到第二个。或者,您可以仅node.raise()将这些元素放在DOM中的所有链接之后:

const width = 800;
const height = 400;

const svg = d3.select("svg").attr("viewBox", [0, 0, width, height]);
let node = svg.selectAll(".node");
let link = svg.selectAll(".link");

const simulation = d3
  .forceSimulation()
  .force("center", d3.forceCenter(width / 2, height / 2))
  .on("tick", (d) => {
    link
      .attr("x1", d => d.source.x)
      .attr("y1", d => d.source.y)
      .attr("x2", d => d.target.x)
      .attr("y2", d => d.target.y);

    node
      .attr("transform", d => `translate(${d.x}, ${d.y})`);
  });

const root = {
  x: width / 2,
  y: height / 2,
  level: 0,
  id: "1"
};

function genChildren(p) {
  const dist = 100 / (p.level + 1);
  const nodes = [{
      x: p.x - dist,
      y: p.y,
      level: p.level + 1,
      id: p.id + "1"
    },
    {
      x: p.x + dist,
      y: p.y,
      level: p.level + 1,
      id: p.id + "2"
    },
    {
      x: p.x,
      y: p.y - dist,
      level: p.level + 1,
      id: p.id + "3"
    },
    {
      x: p.x,
      y: p.y + dist,
      level: p.level + 1,
      id: p.id + "4"
    },
  ];
  const links = nodes.map(v => ({
    source: p.id,
    target: v.id
  }));
  return {
    nodes,
    links
  };
}


function update(nodes, links) {
  link = link.data(links)
    .join(enter => {
      return enter.append("line")
        .attr("stroke", "#000")
        .attr("stroke-width", "1.5px")
        .call(enter => enter.transition().attr("stroke-opacity", 1));
    }, update => update, exit => exit.remove());
  node = node.data(nodes, d => d.id)
    .join(
      enter => {
        const g = enter.append("g");
        g.append("circle")
          .attr("r", 12)
          .attr("cursor", "move")
          .attr("fill", "#ccc")
          .attr("stroke", "#000")
          .attr("stroke-width", "1.5px");
        return g;
      },
      update => update,
      exit => exit.remove(),
    )
    .raise()
    .attr("transform", function(d) {
      return "translate(" + d.x + ", " + d.y + ")";
    })
    .on("click", (ev, d) => {
      if (d.level !== 0) {
        return;
      }
      const childrenInfo = genChildren(root)
      const nodes = d.active ? [root] : [root].concat(childrenInfo.nodes);
      const links = d.active ? [] : childrenInfo.links;
      d.active = !d.active;
      update(nodes, links);
    });

  simulation.nodes(nodes).force("link", d3.forceLink(links).id(node => node.id).strength(-0.01));

  if (simulation.alpha() <= 1) {
    simulation.alpha(1);
    simulation.restart();
  }
}

update([root], [])
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<svg></svg>

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章