【问题标题】:d3 force layout - how to create a more sensible node structured3 force layout - 如何创建更合理的节点结构
【发布时间】:2018-12-23 19:14:55
【问题描述】:

我有一个 d3 强制布局,它从 JSON 文件加载节点并从单独的 CSV 文件加载链接,但有几个(可能相关的)问题:

  1. 生成的结构间隔非常均匀,并且链接中有很多重叠,这使得很难看到哪些节点是连接的。我不知道为什么我会得到这样一个圆形的结构,以及为什么一些只有 1 个链接的节点被放置在与它们的连接节点相反的一侧
  2. 当我拖动一个节点时,没有一个连接节点随之移动。除了我正在移动的一个节点之外,一切似乎都保持静止,而且图表似乎在慢慢放大

您可以在下面的 sn-p 中看到这两个问题:

svg {
  display: block;
  margin: auto;
}

.links line {
  stroke: #999;
  stroke-opacity: 0.6;
  pointer-events: none;
}

.nodes circle {
  stroke: #fff;
  stroke-width: 1.5px;
}

text {
  font-family: sans-serif;
  font-size: 14px;
  font-weight: bold;
}

.primary {
  color: #af0000;
}

.secondary {
  color: #00a700;
}

div.tooltip {
  position: absolute;
  top: 10px;
  right: 10px;
  background-color: white;
  max-width: 200px;
  height: auto;
  padding: 10px;
  border-style: solid;
  border-radius: 2px;
  border-width: 1px;
  box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.5);
}

div.tooltip .Legendary {
  color: #ff9b00;
}

div.tooltip .Epic {
  color: #ac41c2;
}

div.tooltip .Elite {
  color: #058cc3;
}

div.tooltip .Advanced {
  color: #2d9830;
}

div.tooltip table {
  border: 1px solid black;
  border-collapse: collapse;
}

div.tooltip table td {
  padding: 5px;
}
<svg width="960" height="700"></svg>

<script src="https://d3js.org/d3.v4.min.js"></script>

<script>
  const svg = d3.select('svg'),
    width = +svg.attr('width'),
    height = +svg.attr('height');

  const color = d3
    .scaleOrdinal()
    .domain(['Legendary', 'Epic', 'Elite', 'Advanced'])
    .range(['#ff9b00', '#ac41c2', '#058cc3', '#2d9830']);

  const simulation = d3
    .forceSimulation()
    .force('link', d3.forceLink().id(d => d.id))
    .force(
      'charge',
      d3
      .forceManyBody()
      .strength(-20)
      .distanceMax([500])
    )
    .force('center', d3.forceCenter(width / 2, height / 2));

  d3.queue()
    .defer(d3.json, 'https://gist.githubusercontent.com/sho-87/bfe4d69be60454dccfd859b004262381/raw/ac259b468ae6f1140d5c10596f1663c743535b5a/commanders.json')
    .defer(d3.csv, 'https://gist.githubusercontent.com/sho-87/89aa3c48baffc9c388bb98e613b9a5f4/raw/896e24a0187d555d395840d7def3b36b8fb28074/links.csv')
    .await(function(error, commanders, links) {
      const nodeByID = d3.map();
      commanders.nodes.forEach(commander => {
        nodeByID.set(commander.id, commander);
      });

      links.forEach(link => {
        link.source = nodeByID.get(link.primary);
        link.target = nodeByID.get(link.secondary);
      });

      const link = svg
        .append('g')
        .attr('class', 'links')
        .selectAll('line')
        .data(links)
        .enter()
        .append('line')
        .attr('stroke-width', d => Math.sqrt(d.value * 1));

      const node = svg
        .selectAll('.node')
        .data(commanders.nodes)
        .enter()
        .append('g')
        .attr('class', 'nodes')
        .call(
          d3
          .drag()
          .on('start', dragstarted)
          .on('drag', dragged)
          .on('end', dragended)
        )
        .append('circle')
        .attr('r', 8)
        .attr('fill', d => color(d.group))

      simulation.nodes(commanders.nodes).on('tick', ticked);
      simulation.force('link').links(link);

      function 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('transform', d => `translate(${d.x},${d.y})`);
      }
    });

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

我想要的是更不对称的东西,相关/连接的节点放置得更近。理想情况下,我想尽可能减少重叠链接的数量,所以更像这样:

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    缩短以下代码

    // const nodeByID = d3.map();
    // commanders.nodes.forEach(commander => {
    //   nodeByID.set(commander.id, commander);
    // });
    // links.forEach(link => {
    //   link.source = nodeByID.get(link.primary);
    //   link.target = nodeByID.get(link.secondary);
    // });
    
    links = links.map(d => ({source:d.primary, target:d.secondary, value:d.value}));
    

    并将链接对象设置为不是 d3 选择的对象

    simulation.force('link').links(links);
    

    然后您需要使用链接distancestrength 访问器。

    svg {
      display: block;
      margin: auto;
    }
    
    .links line {
      stroke: #999;
      stroke-opacity: 0.6;
      pointer-events: none;
    }
    
    .nodes circle {
      stroke: #fff;
      stroke-width: 1.5px;
    }
    
    text {
      font-family: sans-serif;
      font-size: 14px;
      font-weight: bold;
    }
    
    .primary {
      color: #af0000;
    }
    
    .secondary {
      color: #00a700;
    }
    
    div.tooltip {
      position: absolute;
      top: 10px;
      right: 10px;
      background-color: white;
      max-width: 200px;
      height: auto;
      padding: 10px;
      border-style: solid;
      border-radius: 2px;
      border-width: 1px;
      box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.5);
    }
    
    div.tooltip .Legendary {
      color: #ff9b00;
    }
    
    div.tooltip .Epic {
      color: #ac41c2;
    }
    
    div.tooltip .Elite {
      color: #058cc3;
    }
    
    div.tooltip .Advanced {
      color: #2d9830;
    }
    
    div.tooltip table {
      border: 1px solid black;
      border-collapse: collapse;
    }
    
    div.tooltip table td {
      padding: 5px;
    }
    <svg width="960" height="700"></svg>
    
    <script src="https://d3js.org/d3.v4.min.js"></script>
    
    <script>
      const svg = d3.select('svg'),
        width = +svg.attr('width'),
        height = +svg.attr('height');
    
      const color = d3
        .scaleOrdinal()
        .domain(['Legendary', 'Epic', 'Elite', 'Advanced'])
        .range(['#ff9b00', '#ac41c2', '#058cc3', '#2d9830']);
    
      const simulation = d3
        .forceSimulation()
        .force('link', d3.forceLink().id(d => d.id))
        .force(
          'charge',
          d3
          .forceManyBody()
          .strength(-20)
          .distanceMax([500])
        )
        .force('center', d3.forceCenter(width / 2, height / 2));
    
      d3.queue()
        .defer(d3.json, 'https://gist.githubusercontent.com/sho-87/bfe4d69be60454dccfd859b004262381/raw/ac259b468ae6f1140d5c10596f1663c743535b5a/commanders.json')
        .defer(d3.csv, 'https://gist.githubusercontent.com/sho-87/89aa3c48baffc9c388bb98e613b9a5f4/raw/896e24a0187d555d395840d7def3b36b8fb28074/links.csv')
        .await(function(error, commanders, links) {
    
    // const nodeByID = d3.map();
        // commanders.nodes.forEach(commander => {
        //   nodeByID.set(commander.id, commander);
        // });
        // links.forEach(link => {
        //   link.source = nodeByID.get(link.primary);
        //   link.target = nodeByID.get(link.secondary);
        // });
    
        links = links.map(d => ({source:d.primary, target:d.secondary, value:d.value}));
    
          const link = svg
            .append('g')
            .attr('class', 'links')
            .selectAll('line')
            .data(links)
            .enter()
            .append('line')
            .attr('stroke-width', d => Math.sqrt(d.value * 1));
    
          const node = svg
            .selectAll('.node')
            .data(commanders.nodes)
            .enter()
            .append('g')
            .attr('class', 'nodes')
            .call(
              d3
              .drag()
              .on('start', dragstarted)
              .on('drag', dragged)
              .on('end', dragended)
            )
            .append('circle')
            .attr('r', 8)
            .attr('fill', d => color(d.group))
    
          simulation.nodes(commanders.nodes).on('tick', ticked);
          simulation.force('link').links(links);
    
          function 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('transform', d => `translate(${d.x},${d.y})`);
          }
        });
    
      function dragstarted(d) {
        if (!d3.event.active) simulation.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) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
      }
    </script>

    【讨论】:

      猜你喜欢
      • 2013-04-27
      • 1970-01-01
      • 2015-05-10
      • 1970-01-01
      • 2015-08-09
      • 1970-01-01
      • 1970-01-01
      • 2017-02-15
      • 1970-01-01
      相关资源
      最近更新 更多