力向图节点固定在中心

时间:2018-10-14 15:41:55

标签: javascript d3.js nodes d3-force-directed

对这个问题进行了跟踪Insert text inside Circle in D3 chart

我的节点位于中心位置。我不确定哪个属性在指导我的节点及其x和y坐标。最近,我修改了代码,在圆圈中添加了g层,以便可以将文字和形状一起添加。

数据

https://api.myjson.com/bins/hwtj0

更新代码

    async function d3function() {
        d3.selectAll("svg > *").remove();

        const svg = d3.select("svg");
        file = document.getElementById("selectFile").value;
        console.log("File: " + file)
        var width = 900
        var height = 900 
        svg.style("width", width + 'px').style("height", height + 'px');

        data = (await fetch(file)).json()
        d3.json(file).then(function(data) {

            const links = data.links.map(d => Object.create(d));
            const nodes = data.nodes.map(d => Object.create(d));
            console.log(links.length);
            console.log(nodes.length);
            const simulation = forceSimulation(nodes, links).on("tick", ticked);

            var categorical = [
              { "name" : "schemeAccent", "n": 8},
              { "name" : "schemeDark2", "n": 8},
            ]
            // var colorScale = d3.scaleOrdinal(d3[categorical[6].name])

            var color = d3.scaleOrdinal(d3[categorical[1].name]);


            var drag = simulation => {

                  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;
                  }

                  return d3.drag()
                      .on("start", dragstarted)
                      .on("drag", dragged)
                      .on("end", dragended);
            }

            const link = svg.append("g")
                  .attr("stroke", "#999")
                  .attr("stroke-opacity", 0.6)
                .selectAll("line")
                .data(links)
                .enter().append("line")
                  .attr("stroke-width", d => Math.sqrt(d.value));

            // link.append("title").text(d => d.value);

            // var circles = svg.append("g")
            //       .attr("stroke", "#fff")
            //       .attr("stroke-width", 1.5)
            //     .selectAll(".circle")
            //     .data(nodes)

            // const node = circles.enter().append("circle")
            //       .attr("r", 5)
            //       .attr("fill", d => color(d.group))
            //       .call(drag(simulation));

            const node = svg.append("g")
                  .attr("stroke", "#fff")
                  .attr("stroke-width", 1.5)
                  .selectAll("circles")
                  .data(nodes)
                  .enter()
                .append("g")
                .classed('circles', true)
                .attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');

                node.append("circle")
                .classed('circle', true)
                .attr("r", 5)
                .attr("fill", d => color(d.group))
                .call(drag(simulation));

                node
                  .append("text")
                  .classed('circleText', true)
                  .attr('dy', '0.35em')
                  .attr('dx', 5)
                  .text(d => "Node: " + d.id);

            node.append("title").text(d => "Node: " + d.id);

            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("cx", d => d.x)
                    .attr("cy", d => d.y);
            }

        });

    }



    function forceSimulation(nodes, links) {
      return d3.forceSimulation(nodes)
          .force("link", d3.forceLink(links).id(d => d.id))
          .force("charge", d3.forceManyBody())
          .force("center", d3.forceCenter());
    }

更新后的输出 enter image description here

预期输出 along with node names

更新的HTML

<g stroke="#fff" stroke-width="1.5">
   <g class="circle" cx="-35.89111508769784" cy="131.13965804447696">
      <circle class="circle" r="5" fill="#1b9e77"></circle>
      <text class="circleText" dy="0.35em" dx="5">Node: 0</text>
      <title>Node: 0</title>
   </g>
   <g class="circle" cx="70.97799024729613" cy="-195.71408429254427">
      <circle class="circle" r="5" fill="#d95f02"></circle>
      <text class="circleText" dy="0.35em" dx="5">Node: 3</text>
      <title>Node: 3</title>
   </g>
   [....]
  </g>

1 个答案:

答案 0 :(得分:1)

您必须稍微修改代码,因为它当前假设您正在使用circle元素,您可以在这些元素中使用cxcy指定中心,但是现在您正在使用g元素,它们使用标准的xy坐标。

首先,从g元素中删除转换(这是我的演示代码的剩余部分):

const node = svg.append("g")
  .attr("stroke", "#fff")
  .attr("stroke-width", 1.5)
.selectAll(".circles")  // note - should be .circles!
  .data(nodes)
  .enter()
  .append("g")
  .classed('circles', true)

,然后在ticked()函数中,将node更新代码更改为适用于g元素(没有cx或{{1} }):

cy

演示:

node.attr('transform', d => 'translate(' + d.x + ',' + d.y + ')' )
var json = {"nodes":[{"id":"0","group":0},{"id":"1","group":1},{"id":"2","group":2},{"id":"3","group":3},{"id":"4","group":4},{"id":"5","group":5},{"id":"6","group":6},{"id":"7","group":7},{"id":"8","group":8},{"id":"9","group":9},{"id":"10","group":10},{"id":"11","group":11},{"id":"12","group":12},{"id":"13","group":13},{"id":"14","group":14},{"id":"15","group":15},{"id":"16","group":16},{"id":"17","group":17},{"id":"18","group":18},{"id":"19","group":19}],"links":[{"source":"0","target":"1","value":1},{"source":"0","target":"18","value":1},{"source":"0","target":"10","value":1},{"source":"0","target":"12","value":1},{"source":"0","target":"5","value":1},{"source":"0","target":"8","value":1},{"source":"1","target":"0","value":1},{"source":"1","target":"9","value":1},{"source":"1","target":"4","value":1},{"source":"2","target":"4","value":1},{"source":"2","target":"17","value":1},{"source":"2","target":"13","value":1},{"source":"2","target":"15","value":1},{"source":"3","target":"6","value":1},{"source":"4","target":"14","value":1},{"source":"4","target":"2","value":1},{"source":"4","target":"5","value":1},{"source":"4","target":"19","value":1},{"source":"4","target":"1","value":1},{"source":"5","target":"4","value":1},{"source":"5","target":"0","value":1},{"source":"6","target":"3","value":1},{"source":"7","target":"18","value":1},{"source":"7","target":"16","value":1},{"source":"8","target":"0","value":1},{"source":"9","target":"1","value":1},{"source":"10","target":"0","value":1},{"source":"10","target":"15","value":1},{"source":"12","target":"0","value":1},{"source":"13","target":"15","value":1},{"source":"13","target":"2","value":1},{"source":"14","target":"4","value":1},{"source":"15","target":"13","value":1},{"source":"15","target":"10","value":1},{"source":"15","target":"2","value":1},{"source":"16","target":"7","value":1},{"source":"17","target":"2","value":1},{"source":"18","target":"0","value":1},{"source":"18","target":"7","value":1},{"source":"19","target":"4","value":1},{"source":"19","target":"4","value":1}]};


d3.selectAll("svg > *").remove();

const svg = d3.select("svg");
var width = 900
var height = 900
svg.style("width", width + 'px').style("height", height + 'px');

const links = json.links.map(d => Object.create(d));
const nodes = json.nodes.map(d => Object.create(d));
const simulation = forceSimulation(nodes, links).on("tick", ticked);

var categorical = [
{
  "name": "schemeAccent",
  "n": 8
},
{
  "name": "schemeDark2",
  "n": 8
}, ]

var color = d3.scaleOrdinal(d3[categorical[1].name]);


var drag = simulation => {

  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;
  }

  return d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended);
}

const link = svg.append("g")
  .attr("stroke", "#999")
  .attr("stroke-opacity", 0.6)
  .selectAll("line")
  .data(links)
  .enter().append("line")
  .attr("stroke-width", d => Math.sqrt(d.value));

const node = svg.append("g")
  .attr("stroke", "#fff")
  .attr("stroke-width", 1.5)
  .selectAll(".circles")
  .data(nodes)
  .enter()
  .append("g")
  .classed('circles', true)
  .call(drag(simulation))
//    .attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');

const circle = node.append("circle")
  .classed('circle', true)
  .attr("r", 5)
  .attr("fill", d => color(d.group))

node
  .append("text")
  .classed('circleText', true)
  .attr('dy', '0.35em')
  .attr('dx', 5)
  .text(d => "Node: " + d.id);

node.append("title").text(d => "Node: " + d.id);

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 forceSimulation(nodes, links) {
  return d3.forceSimulation(nodes)
    .force("link", d3.forceLink(links).id(d => d.id))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter());
}
.circleText { fill: black; stroke: none }