亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

D3 Selection 總是返回 Enter() 之后的所有項目,無論它們是否是新的

D3 Selection 總是返回 Enter() 之后的所有項目,無論它們是否是新的

POPMUISE 2023-07-14 10:41:30
對于上下文:我在 React 應用程序中運行 D3,并將 D3 更新模式綁定到 React 生命周期方法。問題: 當我const selection = select(".svg-circle-node-class").data(nodeArray)運行該函數時,D3 總是為我提供數組中的每個節點,無論新節點是否已添加到數組中。我希望該函數selection.enter()為我提供新的節點,并selection.exit()為我提供不存在的節點以供刪除,并且對于直接在此選擇上完成的任何操作而不使用,enter()應該為我提供所有剩余的節點,如下所示:這個問題導致我無法區分新舊項目,這導致我總是重新附加新的 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) 每次更新時每個節點的位置)。
查看完整描述

1 回答

?
阿晨1998

TA貢獻2037條經驗 獲得超6個贊

在D3中,如果您想獲得新的進入/更新/退出選擇,則必須再次執行數據連接。您的代碼中發生的情況是:

  • 您在函數中執行一次數據連接initialize(對于每個圖表元素)。該數據連接將每個節點標記為新節點并返回每個節點,然后您可以緩存這些結果。

  • 在您的update函數中,您每次都使用這些緩存的結果。

update相反,每次圖形更改時,請在 上執行數據連接,而不是在 上執行initialize。一個例子nodeElements

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")

}


private updateGraph(): void {

    // select nodes & edges

    const graphNodes = this.nodeElements

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

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


    // update nodes with their current position

    graphNodes.attr("cx", node => node.x)

        .attr("cy", node => node.y);


    // add newly added nodes if any

    graphNodes.enter()

        .append("circle")

        .attr("class", ".directed-graph-node")

        .attr("stroke", "steelblue")

        .attr("cx", node => node.x)

        .attr("cy", node => node.y)

        .attr("r", 2.5)

        .call(drag<SVGCircleElement, Node>());


    // remove nodes that don't exist anymore

    graphNodes.exit().remove();

}

正如您所看到的,這種模式相當嚴厲。我們可以使用Selection.join()來代替。它允許我們刪除enter和上的重復代碼update并減輕重量。


private updateGraph(): void {

    const graphNodes = this.nodeElements

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

        // data() join()

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

        .join(

        enter => enter.append("circle")

            .attr("class", ".directed-graph-node")

            .attr("stroke", "steelblue")

            .attr("r", 2.5)

            .call(drag<SVGCircleElement, Node>()),

        update => update,

        exit => exit.remove();

        )

        // enter + update past this point

        .attr("cx", node => node.x) 

        .attr("cy", node => node.y)

}


查看完整回答
反對 回復 2023-07-14
  • 1 回答
  • 0 關注
  • 144 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號