d3js v5:使用JSON文件中的组创建SVG

dev_02802

我是d3js的新手,我想从JSON文件创建以下SVG。我正在使用d3js版本5.16。

<svg width="1024" height="700">
    <g id="gp_node1">
        <rect x="0" y="50" width="1024" height="10" id="node1"></rect>
        <g id="gm_node1">
            <circle cy="200" r="10" id="node1"></circle>
            <text x="0" y="10" id="textnode1">node1</text>
        </g>
    </g>
    <g id="gp_node2">
        <rect x="0" y="100" width="1024" height="10" id="node2"></rect>
        <g id="gm_node2">
            <circle cx="200" cy="200" r="10" id="subnode2"></circle>
            <text x="0" y="10" id="textsubnode2">subnode2</text>
        </g>
    </g>
</svg>

它有效(属性除外),但我认为这不是最好的方法,尤其是更新功能(必须对返回值进行注释)。我最终得到的代码是:

var json = {"mainnodes":[{"name":"node1","y":50,"subnodes":[{"name":"subnode1","x":100}]},{"name":"node2","y":100,"subnodes":[{"name":"subnode2","x":200}]}]};
var json2 = {"mainnodes":[{"name":"node1","y":50,"subnodes":[{"name":"subnode1","x":100}]},{"name":"node2","y":150,"subnodes":[{"name":"subnode2","x":200}]}]};

var data = json;
var structure;

var svgContainer = d3.select("body")
                    .append("svg")
                    .attr("width", 1024)
                    .attr("height", 700);
                            
function update(){
    structure = svgContainer.selectAll("g")
                .data(data.mainnodes)
                .join(
                    function(enter) {
                    return enter
                     .append("g")
                     .attr("id",function(d){return "gp_"+d.name;})
                     .append("rect")
                     .attr("x",0)
                     .attr("y",function(d){return d.y;})
                     .attr("width",1024)
                     .attr("height",10)
                     .attr("id",function(d){return d.name;})
                     .each(function(d) {
                          var g = d3.select(this.parentNode)
                         .append("g")
                         .attr("id",function(d){return "gm_"+d.name;})
                         
                          g.data(d.subnodes)
                         .append("circle")
                         .attr("cx",function(d){return d.x;})
                         .attr("cy",200)
                         .attr("r",10)
                         .attr("id",function(d){return d.name;});
                         
                          g.data(d.subnodes)
                         .append("text")
                         .attr("x",0)
                         .attr("y",10)
                         .attr("id",function(d){return "text"+d.name;})
                         .text(function(d){return d.name;});
                     })
                    },
                    function(update) {

                      var s = d3.select("g");
                      s.attr("id",function(d){return "gp_"+d.name;});
                      
                      s.select("rect")
                     .attr("x",0)
                     .attr("y",function(d){return d.y;})
                     .attr("width",1024)
                     .attr("height",10)
                     .attr("id",function(d){return d.name;});
                     
                      var g = s.select("g");
                      g.attr("id",function(d){return "gm_"+d.name;});
                     
                      g.select("circle")
                     .attr("cx",function(d){return d.x;})
                     .attr("cy",200)
                     .attr("r",10)
                     .attr("id",function(d){return d.name;});
                     
                      g.select("text")
                     .attr("x",0)
                     .attr("y",10)
                     .attr("id",function(d){return "text"+d.name;})
                     .text(function(d){return d.name;});
                     
                     //return update;
                    },
                    function(exit) {
                      return exit;
                    }
                )                                                                   
}
d3.select('svg').on('click', function(){
    data = json2;
    update();
})
update();

到目前为止,在所有示例中,我都看到enter函数仅附加了一个元素(因此update和exit始终仅引用此元素)。但是,如果我将append函数拆分为单独的联接,则无法获得所需的SVG结构。我已经尝试了很多变化。也许有人可以告诉我如何正确执行操作。谢谢!

亚历克斯·L

这就是我的处理方式(如果不允许更改数据结构?)。这有帮助吗?如果我可以更改数据结构,则可以更轻松,更清洁地进行操作。由于D3是“数据驱动的文档”,因此我通常从此开始。

const json = {
    "mainnodes":[
      {"name":"node1","y":50,"subnodes":[{"name":"subnode1","x":100}]},
      {"name":"node2","y":75,"subnodes":[{"name":"subnode2","x":25}]}
    ]
};
const json2 = {
    "mainnodes":[
      {"name":"node3","y":50,"subnodes":[{"name":"subnode1","x":100}]},
      {"name":"node4","y":150,"subnodes":[{"name":"subnode2","x":200}]}
    ]
};
const data = [json, json2];
let currIndex = 0;

const svg = d3.select("body")
      .append("svg")
      .attr("width", 1024)
      .attr("height", 700);

function update(data){
  
  const parentGroups = svg.selectAll('.gp')
      .data(data.mainnodes);
      
  parentGroups.enter()
      .append("g")
      .attr('class', 'gp')      
      .merge(parentGroups)
      .transition()
      .duration(750)
        .attr("id",function(d){return "gp_"+d.name;});

  const gr = parentGroups.selectAll('.gr')
      .data(d => [d]);
      
  gr.enter()
      .append("rect")
        .attr('class', 'gr')        
        .merge(gr)
        .transition()
        .duration(750)
          .attr("x",0)
          .attr("y",function(d){return d.y;})
          .attr("width",1024)
          .attr("height",10)
          .attr("id",function(d){return d.name;}); 

  const subGroups = parentGroups.selectAll('.gm')
      .data(d => [d]);
   
  subGroups.enter()
      .append('g')
      .attr('class', 'gm')
      .attr("id",function(d){return "gm_"+d.name;})
      .merge(subGroups)
      .transition()
      .duration(750);

  const c_shapes = subGroups.selectAll('.circles')
      .data(d => d.subnodes);
  
  c_shapes.enter()
      .append('circle')
      .attr('class', 'circles')
      .attr("id",function(d){return d.name;})
      .merge(c_shapes)
      .transition()
      .duration(750)
        .attr("cx",function(d){return d.x;})
        .attr("cy",200)
        .attr("r",10);   
   
  const t_shapes = subGroups.selectAll('.texts')
      .data(d => d.subnodes);
  
  t_shapes.enter()
      .append('text')
      .attr('class', 'texts')
      .attr("id",function(d){return "text"+d.name;})
      .merge(t_shapes)
      .transition()
      .duration(750)      
        .attr("x",function(d){return d.x;})
        .attr("y",230)        
        .text(function(d){return d.name;});
  ///*
  parentGroups.exit().remove();
  gr.exit().remove();       
  subGroups.exit().remove();        
  c_shapes.exit().remove();      
  t_shapes.exit().remove();  
  //*/
}

console.log('start');

//not ideal, I think there is a way to avoid it but it is not coming to mind...
update(data[currIndex]);
update(data[currIndex]);
update(data[currIndex]);

svg.on('click', function(){    
    currIndex = (currIndex + 1) % 2;
    //console.log('click', 'currIndex: ' + currIndex);
    update(data[currIndex]);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<!-- desired output:
<svg width="1024" height="700">
    <g id="gp_node1">
        <rect x="0" y="50" width="1024" height="10" id="node1"></rect>
        <g id="gm_node1">
            <circle cy="200" r="10" id="node1"></circle>
            <text x="0" y="10" id="textnode1">node1</text>
        </g>
    </g>
    <g id="gp_node2">
        <rect x="0" y="100" width="1024" height="10" id="node2"></rect>
        <g id="gm_node2">
            <circle cx="200" cy="200" r="10" id="subnode2"></circle>
            <text x="0" y="10" id="textsubnode2">subnode2</text>
        </g>
    </g>
</svg>
-->

输出:

在此处输入图片说明

一种替代方法,更改数据结构并使用d3-selection-multi

const json = {
    "mainnodes":[
      {"shape":"g","name":"gp_node1",
        "subnodes":[
          {"shape":"rect","name":"rectnode1","width":1024,"height":10,"y":50,"x":0},
          {"shape":"g","name":"gm_node1",
            "subnodes":[
              {"shape":"circle","name":"circlenode1","cy":150,"cx":200,"r":10},
              {"shape":"text","name":"textnode1","text":"subnode1","y":170,"x":200},
            ]
          }
        ]
      },
      {"shape":"g", "name":"gp_node2",
        "subnodes":[
          {"shape":"rect","name":"rectnode2","width":1024,"height":10,"y":75,"x":0},
          {"shape":"g","name":"gm_node2",
            "subnodes":[
              {"shape":"circle","name":"circlenode2","cy":150,"cx":100,"r":10},
              {"shape":"text","name":"textnode2","text":"subnode2","y":170,"x":100},
            ]
          }
        ]
      }
    ]
};
const json2 = {
    "mainnodes":[
      {"shape":"g","name":"gp_node3",
        "subnodes":[
          {"shape":"rect","name":"rectnode3","width":1024,"height":10,"y":50,"x":0},
          {"shape":"g","name":"gm_node3",
            "subnodes":[
              {"shape":"circle","name":"circlenode3","cy":150,"cx":250},
              {"shape":"text","name":"textnode3","text":"subnode3","y":170,"x":250},
            ]
          }
        ]
      },
      {"shape":"g", "name":"gp_node4",
        "subnodes":[
          {"shape":"rect","name":"rectnode4","width":1024,"height":10,"y":100,"x":0},
          {"shape":"g","name":"gm_node2",
            "subnodes":[
              {"shape":"circle","name":"circlenode2","cy":150,"cx":100},
              {"shape":"text","name":"textnode2","text":"subnode4","y":170,"x":100},
            ]
          }
        ]
      }
    ]
};
const data = [json, json2];
let currIndex = 0;

const svg = d3.select("body")
      .append("svg")
      .attr("width", 1024)
      .attr("height", 700);

function update(data){
  
  const parentGroups = svg.selectAll('.gp')
      .data(data.mainnodes);
      
  parentGroups.enter()
      .append("g")
      .attr('class', 'gp')      
      .merge(parentGroups)
      .transition()
      .duration(750)
        .attr("id",function(d){return d.name;});

  const subGroups_d1 = parentGroups.selectAll('.gm')
      .data(d => d.subnodes);
   
  subGroups_d1.enter()
      .append(d => document.createElementNS("http://www.w3.org/2000/svg", d.shape))
      .attr('class', 'gm')
      .attr("id",function(d){return d.name;})
      .merge(subGroups_d1)
      .transition()
      .duration(750)
        .attrs(d => {
          const copy = {...d}
          delete copy.shape;
          delete copy.name;
          return copy;
        });
        
  const subGroups_d2 = subGroups_d1.selectAll('.gn')
      .data(d => d.subnodes || []);
  
  subGroups_d2.enter()
      .append(d => document.createElementNS("http://www.w3.org/2000/svg", d.shape))
      .attr('class', 'gn')
      .attr("id",function(d){return d.name;})
      .merge(subGroups_d2)
      .transition()
      .duration(750)
        .attrs(d => {
          const copy = {...d}
          delete copy.shape;
          delete copy.name;
          return copy;
        })
        .text(d => d.text || ""); 

  ///*
  parentGroups.exit().remove();       
  subGroups_d1.exit().remove();        
  subGroups_d1.exit().remove();        
  //*/
}

console.log('start');

//not ideal, I think there is a way to avoid it but it is not coming to mind...
update(data[currIndex]);
update(data[currIndex]);
update(data[currIndex]);

svg.on('click', function(){    
    currIndex = (currIndex + 1) % 2;
    //console.log('click', 'currIndex: ' + currIndex);
    update(data[currIndex]);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://d3js.org/d3-selection-multi.v1.min.js"></script>

<!-- desired output:
<svg width="1024" height="700">
    <g id="gp_node1">
        <rect x="0" y="50" width="1024" height="10" id="node1"></rect>
        <g id="gm_node1">
            <circle cy="200" r="10" id="node1"></circle>
            <text x="0" y="10" id="textnode1">node1</text>
        </g>
    </g>
    <g id="gp_node2">
        <rect x="0" y="100" width="1024" height="10" id="node2"></rect>
        <g id="gm_node2">
            <circle cx="200" cy="200" r="10" id="subnode2"></circle>
            <text x="0" y="10" id="textsubnode2">subnode2</text>
        </g>
    </g>
</svg>
-->

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章