如何防止节点被点击时一次又一次的全树渲染?Power BI自定义可折叠树使用typescript和d3

r8xiu3jd  于 2023-04-07  发布在  TypeScript
关注(0)|答案(1)|浏览(106)

我正在为Power BI自定义可视化开发一个可折叠的树。我想实现一个切换功能,当我单击它时,它将允许我向上或向下钻取节点。下面我提供了当前用于渲染树的代码和当单击节点时执行的其他方法。我认为当单击节点时,renderTree方法再次从click方法中调用,结果再次渲染整个树。因此,我无法看到正在切换的节点。为此,我需要什么更改或任何其他方法。提前感谢。
以下是我到目前为止尝试的方法,但它没有给予我正确的结果。在这种情况下,测量的数据实际上是一个json树,并且tree()和diagonal()已经在类Visual的全局范围内声明,就像这样:

// Declare an interface for the links/connections
interface LinkCoords {
  x: number;
  y: number;
}

// Declare an interface for a tree node
interface TreeNode {
  name: string;
  children?: TreeNode[];
  // Dynamic addition of any incoming columns from analyze by data role
  [key: string]: number | string | undefined | TreeNode[];
}

// Create a tree layout
  private tree = d3
    .tree()
    // Height and width of the separations
    .nodeSize([50, 300])
    // Wether or not the nodes have same parent, there should be a separation of 2 units between nodes
    .separation(function separation(a, b) {
      return a.parent == b.parent ? 2 : 2;
    });

  // Links properties
  private diagonal = d3
    .linkHorizontal<{}, LinkCoords>()
    .x((d) => d.y)
    .y((d) => d.x);
// Function that would render the tree on the canvas
  private renderTree(measuredData: TreeNode) {
    // Compute the hierarchy
    const root = d3.hierarchy(measuredData);

    // Call the tree function
    this.tree(root);

    // Node variable
    const node = this.treeContainer
      .selectAll(".node")
      .data(root.descendants())
      .enter()
      .append("g")
      .attr("transform", function (d: any) {
        return "translate(" + (d.y + 20) + "," + (d.x + 307) + ")";
      })
      .attr("font-size", 10)
      .on("click", (event, d) => this.click(d.data));

    // Node properties
    node
      .append("rect")
      .attr("width", 150)
      .attr("height", 50)
      .style("stroke", function (d) {
        return d.children ? "#5CCE8A" : "#D45F5F";
      })
      .attr("fill", "grey");

    // Node text properties
    node
      .append("text")
      .attr("x", 70)
      .attr("y", 25)
      .attr("text-anchor", "middle")
      .text(
        (d) => `name: ${d.data.name}, value: ${d.data.V}, import: ${d.data.I}`
      )
      .attr("fill", "white");

    // Create links
    const link = this.treeContainer
      .selectAll(".link")
      .data(root.links())
      .enter();

    // Define the path source and target for the links
    link
      .append("path")
      .attr("class", "link")
      .attr("fill", "none")
      .attr("stroke", "white")
      .attr("d", (d) => {
        // Define the source
        const source: LinkCoords = {
          x: (d.source as any).x + 332,
          y: (d.source as any).y + 170,
        };
        // Define the target
        const target: LinkCoords = {
          x: (d.target as any).x + 332,
          y: (d.target as any).y + 20,
        };
        return this.diagonal({ source, target });
      })
      .attr("stroke-width", "1px")
      .attr("stroke-dasharray", "1, 0");
  }

  // Toggle the node
  private click(d: any) {
    if (d.children) {
      // If the clicked node has children
      d._children = d.children; // Hide its children
      d.children = null;
    } else {
      // If the clicked node does not have children
      d.children = d._children; // Show its children
      d._children = null;
    }
    this.renderTree(d);
  }
wydwbb8l

wydwbb8l1#

你不能看到被切换的节点的原因是因为每次你点击一个节点,你都在用更新的数据重新渲染整个树。你应该通过更新需要切换的节点的属性来修改现有的树,而不是重新渲染整个树。
为了实现这一点,您可以为每个节点添加一个“collapsed”属性,该属性将根据节点是折叠还是展开而设置为true或false。然后,在click函数中,您可以切换单击的节点及其子节点的“collapsed”属性,然后更新SVG中相应节点的属性。
你可以改变一些东西,

private click(d: any) {
    if (d.children || d._children) {
        if (d.collapsed) {
            d.children = d._children;
            d._children = null;
            d.collapsed = false;
        } else {
            // If the clicked node is collapsed, expand it
            d._children = d.children;
            d.children = null;
            d.collapsed = true; // using this property to change the behavior on click
        }

        const descendants = this.treeContainer
            .selectAll(".node")
            .data(this.tree(root).descendants());

        descendants
            .select("rect")
            .style("stroke", function (d) {
                return d.children || d._children ? "#5CCE8A" : "#D45F5F";
            });

        const links = this.treeContainer.selectAll(".link").data(this.tree(root).links());
        links.exit().remove(); // removes the elements for those deleted in data 
        links.enter() 
            .append("path")
            .attr("class", "link")
            .attr("fill", "none")
            .attr("stroke", "white")
            .attr("stroke-width", "1px")
            .attr("stroke-dasharray", "1, 0")
            .merge(links)
            .attr("d", (d) => {
                const source: LinkCoords = {
                    x: (d.source as any).x + 332,
                    y: (d.source as any).y + 170,
            };
                const target: LinkCoords = {
                    x: (d.target as any).x + 332,
                    y: (d.target as any).y + 20,
            };
                return this.diagonal({ source, target });
            });

    }
}

相关问题