【问题标题】:Hide attached child nodes onClick()隐藏附加的子节点 onClick()
【发布时间】:2022-01-07 23:16:52
【问题描述】:

我正在处理一个小型 D3.js 图,如果单击父级较大节点,我想隐藏连接的较小节点。到目前为止,我尝试了几种方法,首先过滤链接以接收源节点,这可能是我最好的尝试。

很遗憾我收到newLinks.map is not a function error,要么我完全误解了地图功能,要么就是错过了最后的和平。

  function onClick(event, d) {
        const newLinks = link.filter(link => link.target.id === d.id);     
        console.log(newLinks)

        const newNodes = newLinks.map(link => data.nodes.find(newNode => newNode.id === link.source.id))
        console.log(newNodes)
    }

问题: 我怎样才能达到我所追求的目标?如果单击父节点,我想隐藏较小的附加节点。

var svg = d3.select("body").append("svg")
  .attr("width", window.innerWidth)
  .attr("height", window.innerHeight)

var data = {
  "nodes": [{
      "id": "A",
      "type": "parent"
    },
    {
      "id": "B",
      "type": "parent"
    },
    {
      "id": "C",
      "type": "child"
    },
    {
      "id": "D",
      "type": "child"
    },
    {
      "id": "E",
      "type": "child"
    },
  ],
  "links": [{
      "source": "A",
      "target": "B",
      "distance": 125
    },
    {
      "source": "C",
      "target": "A",
      "distance": 20
    },
    {
      "source": "D",
      "target": "A",
      "distance": 20
    },
    {
      "source": "E",
      "target": "B",
      "distance": 20
    },
  ]
}

var force = d3.forceSimulation()
  .force("link", d3.forceLink().id(function(d) {
    return d.id
  }).distance(function(d) {
    return d.distance
  }))
  .force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
  .force("charge", d3.forceManyBody().strength(-1000))
  .force("collision", d3.forceCollide().radius(setSize))

var linksContainer = svg.append("g").attr("class", "linksContainer")
var nodesContainer = svg.append("g").attr("class", "nodesContainer")

initialize()

function initialize() {
  link = linksContainer.selectAll("line")
    .data(data.links)
    .join("line")
    .attr("class", "line")

  node = nodesContainer.selectAll(".node")
    .data(data.nodes, d => d.id)
    .join("g")
    .attr("class", "node")
    .call(d3.drag()
      .on("start", dragStarted)
      .on("drag", dragged)
      .on("end", dragEnded)
    )

  node.selectAll("circle")
    .data(d => [d])
    .join("circle")
    .attr("r", setSize)
    .on("click", onClick)

  force
    .nodes(data.nodes)
    .on("tick", ticked)

  force
    .force("link")
    .links(data.links)
}


function setSize(d) {
  switch (d.type) {
    case "parent":
      return 40
    case "child":
      return 20
    default:
      return 40
  }
}

function onClick(event, d) {
  const newLinks = link.filter(link => link.target.id === d.id);
  console.log(newLinks)

  const newNodes = newLinks.map(link => data.nodes.find(newNode => newNode.id === link.source.id))
  console.log(newNodes)
}

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("transform", function(d) {
      return "translate(" + d.x + ", " + d.y + ")";
    });
}

function dragStarted(event, d) {
  if (!event.active) force.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) force.alphaTarget(0);
  d.fx = undefined;
  d.fy = undefined;
}
body {
  overflow: hidden;
  background: #e6e7ee;
  margin: 0;
}

circle {
  fill: whitesmoke;
  stroke: black;
}

line {
  stroke: black;
}
<script src="https://d3js.org/d3.v7.js"></script>

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    您需要了解数据数组和 d3 选择之间的区别。在您的代码中,linknode 是代表圆和线的 d3 选择。它们不是基础数据的表示。

    但是,它们确实提供了一些在这种情况下有用的功能。例如,您可以使用.each(...) 而不是forEach 来循环遍历元素,或者使用.data() 来获取d3 选择表示为数组的对象,因此link.data().map 绝对是一个有效的函数。

    我通过将属性isVisible 添加到确定可见性的每个节点或链接来实现您想要的。但是,这绝对不是最好的或唯一的方法,因此请随意探索替代方案。

    var svg = d3.select("body").append("svg")
      .attr("width", window.innerWidth)
      .attr("height", window.innerHeight)
    
    var data = {
      "nodes": [{
          "id": "A",
          "type": "parent"
        },
        {
          "id": "B",
          "type": "parent"
        },
        {
          "id": "C",
          "type": "child"
        },
        {
          "id": "D",
          "type": "child"
        },
        {
          "id": "E",
          "type": "child"
        },
      ],
      "links": [{
          "source": "A",
          "target": "B",
          "distance": 125
        },
        {
          "source": "C",
          "target": "A",
          "distance": 20
        },
        {
          "source": "D",
          "target": "A",
          "distance": 20
        },
        {
          "source": "E",
          "target": "B",
          "distance": 20
        },
      ]
    }
    
    var force = d3.forceSimulation()
      .force("link", d3.forceLink().id(function(d) {
        return d.id
      }).distance(function(d) {
        return d.distance
      }))
      .force("center", d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2))
      .force("charge", d3.forceManyBody().strength(-1000))
      .force("collision", d3.forceCollide().radius(setSize))
    
    var linksContainer = svg.append("g").attr("class", "linksContainer")
    var nodesContainer = svg.append("g").attr("class", "nodesContainer")
    
    initialize()
    
    function initialize() {
      const links = data.links
        .filter(link => link.isVisible !== false);
      const nodes = data.nodes.filter(node =>
        node.isVisible !== false);
    
      link = linksContainer.selectAll("line")
        .data(links)
        .join("line")
        .attr("class", "line")
    
      node = nodesContainer.selectAll(".node")
        .data(nodes, d => d.id)
        .join("g")
        .attr("class", "node")
        .call(d3.drag()
          .on("start", dragStarted)
          .on("drag", dragged)
          .on("end", dragEnded)
        )
    
      node.selectAll("circle")
        .data(d => [d])
        .join("circle")
        .attr("r", setSize)
        .on("click", onClick)
    
      force
        .nodes(nodes)
        .on("tick", ticked)
    
      force
        .force("link")
        .links(links)
    }
    
    
    function setSize(d) {
      switch (d.type) {
        case "parent":
          return 40
        case "child":
          return 20
        default:
          return 40
      }
    }
    
    function onClick(event, d) {
      link.data()
        .forEach(link => {
          link.isVisible = link.target.id === d.id;
        });
      const visibleNodeIds = [
        d.id,
        ...link.data()
        .filter(l => l.isVisible)
        .map(l => l.source.id)
      ];
    
      node.data()
        .forEach(node => {
          node.isVisible = visibleNodeIds.includes(node.id);
        });
    
      initialize();
    }
    
    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("transform", function(d) {
          return "translate(" + d.x + ", " + d.y + ")";
        });
    }
    
    function dragStarted(event, d) {
      if (!event.active) force.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) force.alphaTarget(0);
      d.fx = undefined;
      d.fy = undefined;
    }
    body {
      overflow: hidden;
      background: #e6e7ee;
      margin: 0;
    }
    
    circle {
      fill: whitesmoke;
      stroke: black;
    }
    
    line {
      stroke: black;
    }
    <script src="https://d3js.org/d3.v7.js"></script>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-18
      • 1970-01-01
      • 1970-01-01
      • 2022-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多