首页 > 解决方案 > d3.js - 选择强制布局组不更新节点

问题描述

下面的示例应在选择底部的组后更新节点。但选择工作正常,svg 上的节点未更新!

force_json()
function force_json(){
  var svg = d3.select('body').append('svg')
  .attr('width',200).attr('height',100)
  .style('border','1px solid red'),
      width = +svg.attr("width"),
      height = +svg.attr("height");

  var simulation = d3.forceSimulation()
  .force("link", d3.forceLink().id(function(d) { return d.id; }))
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2));

  var color = d3.scaleOrdinal(d3.schemeCategory10);

  var jsonobj = {
    "nodes": [
      {"id": "Myriel", "group": 1},
      {"id": "Napoleon", "group": 2},
      {"id": "Mlle.Baptistine", "group": 3},
      {"id": "Mme.Magloire", "group": 1},
    ],
    "links": [
      {"source": "Napoleon", "target": "Myriel", "value": 10},
      {"source": "Mlle.Baptistine", "target": "Myriel", "value": 8},
      {"source": "Mme.Magloire", "target": "Myriel", "value": 10},
    ]
  }

  // d3.json("miserables.json",function(error, graph) {
  //   if (error) throw error;

  // });
  process_data(jsonobj)
  function process_data(graph) {
    var currNodes = graph.nodes
    var currLinks = graph.links

    var nodesByGroup = d3.group(graph.nodes,d => d.group)

    var catMenu = d3.select("body").append('div')
    catMenu
      .append("select")
      .selectAll("option")
      .data(nodesByGroup)
      .enter()
      .append("option")
      .attr("value", function(d,i) {
      return d[0];
    })
      .text(function(d,i){
      return d[0];
    })

    var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(currLinks)
    .enter().append("line")
    .attr('stroke','#aaa')

    var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(currNodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr('pointer-events','all')
    .attr('stroke','none')
    .attr('stroke-wdith',40)
    .attr("fill", function(d) { return color(d.group);})
    .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));

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

    node.append("text")
      .attr("dx", 12)
      .attr("dy", ".35em")
      .text(function(d) {return d.id;})

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

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

    function ticked() {
      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; });
    }

    catMenu.on('change', function(){
      var selectedGroup = +d3.select(this)
      .select("select")
      .property("value");
      currNodes = filterNodes(selectedGroup);
    });

    function filterNodes(group) {
      var filteredNodes = nodesByGroup.get(group)
      return filteredNodes;
    }
  }

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

  function dragged(event,d) {
    d.fx = event.x;
    d.fy = event.y;
  }

  function dragended(event,d) {
    if (!event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

标签: d3.js

解决方案


在组选择时重新渲染过滤的节点和链接:

force_json()

function force_json(){
  var svg = d3.select('body').append('svg')
  .attr('width',200).attr('height',200)
  .style('border','1px solid red'),
      width = +svg.attr("width"),
      height = +svg.attr("height");

  var color = d3.scaleOrdinal(d3.schemeCategory10);

  var jsonobj = {
    "nodes": [
      {"id": "Myriel", "group": 1},
      {"id": "Napoleon", "group": 2},
      {"id": "Mlle.Baptistine", "group": 3},
      {"id": "Mme.Magloire", "group": 1},
      {"id": "A", "group": 2},
      {"id": "B", "group": 3},
      {"id": "C", "group": 3}
    ],
    "links": [
      {"source": "Napoleon", "target": "Myriel", "value": 10},
      {"source": "Napoleon", "target": "A", "value": 10},
      {"source": "Mlle.Baptistine", "target": "Myriel", "value": 8},
      {"source": "Mlle.Baptistine", "target": "B", "value": 8},
      {"source": "Mme.Magloire", "target": "Myriel", "value": 10},
      {"source": "A", "target": "B", "value": 10},
      {"source": "C", "target": "B", "value": 10},
    ]
  }

  process_data(jsonobj)
  
  function process_data(graph) {
    var nodesByGroup = d3.group(graph.nodes,d => d.group)
    
    var catMenu = d3.select("body").append('div')
    catMenu
      .append("select")
      .selectAll("option")
      .data(nodesByGroup)
      .enter()
      .append("option")
      .attr("value", function(d,i) {
      return d[0];
    })
      .text(function(d,i){
      return d[0];
    })
    catMenu.select('select').append('option').text('all').attr("selected", "selected");

  const updateGraph = (nodes, links) => {
    const simulation = d3.forceSimulation()
  .force("link", d3.forceLink().id(function(d) { return d.id; }))
  .force("charge", d3.forceManyBody())
  .force("center", d3.forceCenter(width / 2, height / 2));
    
    svg.selectAll('g').remove();
    var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(links)
    .enter().append("line")
    .attr('stroke','#aaa')

    var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(nodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr('pointer-events','all')
    .attr('stroke','none')
    .attr('stroke-wdith',40)
    .attr("fill", function(d) { return color(d.group);})
    .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));

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

    node.append("text")
      .attr("dx", 12)
      .attr("dy", ".35em")
      .text(function(d) {return d.id;})

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

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

    function ticked() {
      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 dragstarted(event,d) {
    if (!event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  function dragged(event,d) {
    d.fx = event.x;
    d.fy = event.y;
  }

  function dragended(event,d) {
    if (!event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }
}

    updateGraph(graph.nodes, graph.links);
    catMenu.on('change', function(){
      var selectedGroup = d3.select(this)
      .select("select")
      .property("value");
      
      let nodes, links;
      if (selectedGroup === 'all') {
        nodes = graph.nodes;
        links = graph.links;
      } else {
        const group = parseInt(selectedGroup);
        nodes = graph.nodes.filter(n => n.group === group);
        console.log(graph.links)
        links = graph.links.filter(link => {
        const source = nodes.find(n => n.id === link.source.id);
        const target = nodes.find(n => n.id === link.target.id);
        return source && target;
        })
      }
       updateGraph(nodes, links);
    });
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>


推荐阅读