D3 Selection 总是返回 Enter() 之后的所有项目,无论它们是否是新的

对于上下文:我在 React 应用程序中运行 D3,并将 D3 更新模式绑定到 React 生命周期方法。

问题: 当我const selection = select(".svg-circle-node-class").data(nodeArray)运行该函数时,D3 总是为我提供数组中的每个节点,无论新节点是否已添加到数组中。我希望该函数selection.enter()为我提供新的节点,并selection.exit()为我提供不存在的节点以供删除,并且对于直接在此选择上完成的任何操作而不使用,enter()应该为我提供所有剩余的节点,如下所示:

http://img1.mukewang.com/64b0b5f500017af706260421.jpg

这个问题导致我无法区分新旧项目,这导致我总是重新附加新的 SVG 元素,因此每次状态发生变化时都会复制所有内容。我只想更新这些元素的值。


我的具体情况如下:我有一个图的实现,其结构如下:


class Graph {

  nodes: Node[];

}


class DirectedEdge {

  from: Node;

  to: Node;

}


class Node {

  x: number;

  y: number;

  edges: DirectedEdge[];

  id: string; // unique for each node

}

在我的反应组件中,我通过在组件状态中保存图形的实例来跟踪图形的状态,如下所示:


const n1, n2: Node = ...;


interface IState {

  g: Graph = new Graph(n1, n2), // constructor connects two graphs with an edge

  degree: number // a parameter to change some shapes

}

我将图表的初始化与componentDidMount生命周期方法联系起来,这是执行此操作的函数:


    /** Once HTML elements have loaded, this method is run to initialize the SVG elements using D3. */

    private initializeGraph(): void {

        const mainGroup = select(this.svgElement.current)

            .append("g")

            .attr("id", "main");


        // append nodes svg group

        this.nodeElements = mainGroup.append("g")

            .attr("id", "nodes")

            .selectAll<SVGCircleElement, Node>(".directed-graph-node")

            .data<Node>(this.state.g.nodes, _ => _.id);


        // append edges svg group

        this.edgeElements = mainGroup.append("g")

            .attr("id", "edges")

            .selectAll<SVGPathElement, DirectedEdge>(".directed-graph-edge")

            .data<DirectedEdge>(this.state.g.nodes.flatMap(_ => _.edges), _ => _.id);


    }

我将一个updateGraph函数与componentDidUpdate生命周期函数绑定在一起,导致每次状态发生变化时都会调用它(即,在本例中,是由参数“ Degree”变化引起的。但我希望能够更新(x ,y) 每次更新时每个节点的位置)。


POPMUISE
浏览 72回答 1
1回答

阿晨1998

在D3中,如果您想获得新的进入/更新/退出选择,则必须再次执行数据连接。您的代码中发生的情况是:您在函数中执行一次数据连接initialize(对于每个图表元素)。该数据连接将每个节点标记为新节点并返回每个节点,然后您可以缓存这些结果。在您的update函数中,您每次都使用这些缓存的结果。update相反,每次图形更改时,请在 上执行数据连接,而不是在 上执行initialize。一个例子nodeElements:private initializeGraph(): void {&nbsp; &nbsp; const mainGroup = select(this.svgElement.current)&nbsp; &nbsp; &nbsp; &nbsp; .append("g")&nbsp; &nbsp; &nbsp; &nbsp; .attr("id", "main");&nbsp; &nbsp; // append nodes svg group&nbsp; &nbsp; this.nodeElements = mainGroup.append("g")&nbsp; &nbsp; &nbsp; &nbsp; .attr("id", "nodes")}private updateGraph(): void {&nbsp; &nbsp; // select nodes & edges&nbsp; &nbsp; const graphNodes = this.nodeElements&nbsp; &nbsp; &nbsp; &nbsp; .selectAll<SVGCircleElement, Node>(".directed-graph-node")&nbsp; &nbsp; &nbsp; &nbsp; .data<Node>(this.state.g.nodes, _ => _.id);&nbsp; &nbsp; // update nodes with their current position&nbsp; &nbsp; graphNodes.attr("cx", node => node.x)&nbsp; &nbsp; &nbsp; &nbsp; .attr("cy", node => node.y);&nbsp; &nbsp; // add newly added nodes if any&nbsp; &nbsp; graphNodes.enter()&nbsp; &nbsp; &nbsp; &nbsp; .append("circle")&nbsp; &nbsp; &nbsp; &nbsp; .attr("class", ".directed-graph-node")&nbsp; &nbsp; &nbsp; &nbsp; .attr("stroke", "steelblue")&nbsp; &nbsp; &nbsp; &nbsp; .attr("cx", node => node.x)&nbsp; &nbsp; &nbsp; &nbsp; .attr("cy", node => node.y)&nbsp; &nbsp; &nbsp; &nbsp; .attr("r", 2.5)&nbsp; &nbsp; &nbsp; &nbsp; .call(drag<SVGCircleElement, Node>());&nbsp; &nbsp; // remove nodes that don't exist anymore&nbsp; &nbsp; graphNodes.exit().remove();}正如您所看到的,这种模式相当严厉。我们可以使用Selection.join()来代替。它允许我们删除enter和上的重复代码update并减轻重量。private updateGraph(): void {&nbsp; &nbsp; const graphNodes = this.nodeElements&nbsp; &nbsp; &nbsp; &nbsp; .selectAll<SVGCircleElement, Node>(".directed-graph-node")&nbsp; &nbsp; &nbsp; &nbsp; // data() join()&nbsp; &nbsp; &nbsp; &nbsp; .data<Node>(this.state.g.nodes, _ => _.id)&nbsp; &nbsp; &nbsp; &nbsp; .join(&nbsp; &nbsp; &nbsp; &nbsp; enter => enter.append("circle")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .attr("class", ".directed-graph-node")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .attr("stroke", "steelblue")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .attr("r", 2.5)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .call(drag<SVGCircleElement, Node>()),&nbsp; &nbsp; &nbsp; &nbsp; update => update,&nbsp; &nbsp; &nbsp; &nbsp; exit => exit.remove();&nbsp; &nbsp; &nbsp; &nbsp; )&nbsp; &nbsp; &nbsp; &nbsp; // enter + update past this point&nbsp; &nbsp; &nbsp; &nbsp; .attr("cx", node => node.x)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; .attr("cy", node => node.y)}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript