D3 x和y坐标应用于上次出现的元素而不是组

时间:2018-03-25 10:58:01

标签: javascript d3.js

我有一个强制定向图,其中每个节点都是一个组,并包含一个foreignObject和一个img。 x和y coordiantes应用于img标签,但我想将它们放在foreignObject或g上。我不确定如何实现这一目标。

我尝试将x和y坐标设置为foreignObject,但它们始终以NaN形式返回。

const plot = (data) => {
  data.nodes = data.nodes.map((d, index) => {
    d['id'] = index;
    return d;
  });
  const margin = {
    top: 20,
    right: 20,
    bottom: 10,
    left: 100
  };
  const width = Math.max((((window.innerWidth / 100) * 80) - margin.right - margin.left), 700);
  const height = ((window.innerHeight / 100) * 80) - margin.bottom - margin.top;
  const svg = d3.select('svg')
    .attr('width', width + margin.left + margin.right + 100)
    .attr('height', height + margin.top + margin.bottom + 100)
    .append('g')
    .attr('transform',
      `translate(${margin.left}, ${margin.top})`);

  const simulation = d3.forceSimulation()
    .force('link', d3.forceLink().id(function (d) { return d.id; }).distance(100).strength(1))
    .force('charge', d3.forceManyBody())
    .force('center', d3.forceCenter(width / 2, height / 2));

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

  const dragged = d => {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  const dragended = d => {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }

  const ticked = () => {
    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("x", function (d) { return d.x = Math.max(5, Math.min(width - 5, d.x)); })
      .attr("y", function (d) { return d.y = Math.max(5, Math.min(height - 5, d.y)); });
  }

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

  const node = svg.append('g')
    .attr('class', 'nodes')
    .selectAll('.node-group')
    .data(data.nodes)
    .enter()
    .append('g')
    .append('foreignObject')
    .attr('class', 'node-group')
    .attr('width', '10')
    .attr('height', '10')
    .insert('xhtml:img')
    .attr('src', 'flags/blank.png')
    .attr('class', d => `flag flag-${d.code}`)
    .call(d3.drag()
      .on('start', dragstarted)
      .on('drag', dragged)
      .on('end', dragended));

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

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

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

}

1 个答案:

答案 0 :(得分:1)

如果您查看node选择...

const node = svg.append('g')
    .attr('class', 'nodes')
    .selectAll('.node-group')
    .data(data.nodes)
    .enter()
    .append('g')
    .append('foreignObject')
    .attr('class', 'node-group')
    .attr('width', '10')
    .attr('height', '10')
    .insert('xhtml:img')
    .attr('src', 'flags/blank.png')
    .attr('class', d => `flag flag-${d.code}`)
    .call(d3.drag()
        .on('start', dragstarted)
        .on('drag', dragged)
        .on('end', dragended));

...你会看到它是image的选择。由于您要将xy属性应用于foreignObject,只需将其打破:

const node = svg.append('g')
    .attr('class', 'nodes')
    .selectAll('.node-group')
    .data(data.nodes)
    .enter()
    .append('g')
    .append('foreignObject')
    .attr('class', 'node-group')
    .attr('width', '10')
    .attr('height', '10');

node.insert('xhtml:img')
    .attr('src', 'flags/blank.png')
    .attr('class', d => `flag flag-${d.code}`)
    .call(d3.drag()
        .on('start', dragstarted)
        .on('drag', dragged)
        .on('end', dragended));

这样,nodeforeignObjects的选择。