乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      D3.js畫(huà)思維導(dǎo)圖(轉(zhuǎn))

       ooAAMM 2019-03-23

      思維導(dǎo)圖的節(jié)點(diǎn)具有層級(jí)關(guān)系和隸屬關(guān)系,很像枝葉從樹(shù)干伸展開(kāi)來(lái)的形狀。在前面講解布局的時(shí)候,提到有五個(gè)布局是由層級(jí)布局?jǐn)U展來(lái)的,其中的樹(shù)狀圖(tree layout)和集群圖(cluster layout)布局制作出來(lái)的圖具有“樹(shù)形”。因此,可以憑借這兩種布局來(lái)制作思維導(dǎo)圖。

      1. 構(gòu)造思路

      樹(shù)狀圖布局,將一個(gè)具有層級(jí)關(guān)系的對(duì)象root轉(zhuǎn)換成節(jié)點(diǎn)數(shù)組nodes時(shí),情況如下。有一個(gè)root對(duì)象:

      {  
          name: "node1",  
          children:   
      [  
                  { name: "node2" },  
                  { name: "node3" }  
          ]  
      }  

      經(jīng)樹(shù)狀圖布局轉(zhuǎn)換后,得到的節(jié)點(diǎn)數(shù)組nodes如下:

      [  
      {  
              name: "node1",  
              children:   
              [  
                  { name: "node2" },  
                  { name: "node3" }  
              ]  
          },  
          { name: "node2" },  
          { name: "node3" }  
      ]  

      下圖是上述節(jié)點(diǎn)數(shù)組的示意圖。由于 node1 具有子節(jié)點(diǎn),可作為開(kāi)關(guān)使用,點(diǎn)擊 node1 才會(huì)展現(xiàn) node2 和 node3。

      問(wèn)題是:怎樣制作一個(gè)“開(kāi)關(guān)”,使得點(diǎn)擊樹(shù)狀圖中的某個(gè)節(jié)點(diǎn)時(shí),樹(shù)狀圖更新并顯示出被點(diǎn)擊節(jié)點(diǎn)的子節(jié)點(diǎn)。

      我們知道,樹(shù)狀圖的層級(jí)關(guān)系是由每一個(gè)對(duì)象的children屬性決定的(當(dāng)然,也可以通過(guò)tree.children()修改這一點(diǎn)),也就是說(shuō),如果某一個(gè)節(jié)點(diǎn)的children值為空,則再次用布局計(jì)算時(shí),其子節(jié)點(diǎn)就不會(huì)進(jìn)入節(jié)點(diǎn)數(shù)組nodes了。例如,將root改為:

      {  
          name: "node1",  
          children: null  
      }  

      則得到的節(jié)點(diǎn)數(shù)組nodes里將沒(méi)有node2和node3節(jié)點(diǎn)。也就是說(shuō),“開(kāi)關(guān)”只要將被點(diǎn)擊節(jié)點(diǎn)的children設(shè)置為null即可。但是,由于將來(lái)可能還要用到children節(jié)點(diǎn),可設(shè)一臨時(shí)變量_children保存此值,例如:

      {  
          name: "node1",  
          children: null  
             _children:       /* 臨時(shí)變量 */  
             [  
                  { name: "node2" },  
                  { name: "node3" }  
          ]  
        
      }  

      樹(shù)狀圖布局不會(huì)認(rèn)為_(kāi)children是保存子節(jié)點(diǎn)的變量,只把它看做是一般的變量而保存下來(lái),因此節(jié)點(diǎn)數(shù)組nodes里只有一個(gè)節(jié)點(diǎn)。根據(jù)上面的思路,寫(xiě)一個(gè)開(kāi)關(guān)切換函數(shù)如下。

      //切換開(kāi)關(guān),d 為被點(diǎn)擊的節(jié)點(diǎn)  
      function toggle(d){  
        
      if(d.children){  
      //如果有子節(jié)點(diǎn)  
              d._children = d.children; //將該子節(jié)點(diǎn)保存到 _children  
              d.children = null;  //將子節(jié)點(diǎn)設(shè)置為null  
            
      }else{  
      //如果沒(méi)有子節(jié)點(diǎn)  
              d.children = d._children; //從 _children 取回原來(lái)的子節(jié)點(diǎn)   
              d._children = null; //將 _children 設(shè)置為 null  
          }  
      }  

      每次開(kāi)關(guān)狀態(tài)切換時(shí),都要重新調(diào)用布局重新計(jì)算節(jié)點(diǎn)的位置,也就是說(shuō),要有一個(gè)重繪函數(shù)能夠處理數(shù)據(jù)發(fā)生更新的情況。這就又要用到【選擇集與數(shù)據(jù) - 第 5 章】的處理模板,重繪函數(shù)的部分代碼如下,尤其要注意開(kāi)關(guān)函數(shù)是如何被使用的。

      //重繪函數(shù)  
       function redraw(source){  
        
          //重新計(jì)算節(jié)點(diǎn)和連線(xiàn)  
          var nodes = tree.nodes(root);  
          var links = tree.links(nodes);  
        
      //獲取節(jié)點(diǎn)的update部分  
          var nodeUpdate = svg.selectAll(".node")  
                              .data(nodes, function(d){ return d.name; });  
        
          //獲取節(jié)點(diǎn)的enter部分  
          var nodeEnter = nodeUpdate.enter();  
        
      //在給enter部分添加新的節(jié)點(diǎn)時(shí),添加監(jiān)聽(tīng)器,應(yīng)用開(kāi)關(guān)切換函數(shù)  
      nodeEnter.append("g")  
                 .on("click", function(d) {   
      toggle(d);   
      redraw(d);   
      });  
        
      /*************** 
              省略 
      ***************/  
      }  

      每一個(gè)被新添加的節(jié)點(diǎn),都會(huì)響應(yīng)click事件。當(dāng)某個(gè)節(jié)點(diǎn)被點(diǎn)擊時(shí),如果它具有子節(jié)點(diǎn),則在開(kāi)關(guān)切換函數(shù)的作用下,root對(duì)象被修改了,然后調(diào)用重繪函數(shù)后,新的樹(shù)狀圖將被繪制。如此一來(lái),樹(shù)狀圖具有開(kāi)關(guān)功能,也就可以當(dāng)做思維導(dǎo)圖使用了。

      2. 制作思維導(dǎo)圖

      首先,要有一個(gè)具有層級(jí)關(guān)系的 JSON 文件,本文使用:learn.json

      {
      "name":"如何學(xué)習(xí)D3",
      "children":
      [
          { 
            "name":"預(yù)備知識(shí)" , 
              "children":
              [
                      {"name":"HTML & CSS" },
                      {"name":"JavaScript" },
                      {"name":"DOM" },
                      {"name":"SVG" }
              ] 
            },
            
          { 
              "name":"安裝" , 
              "children":
              [
                  {
                      "name":"記事本軟件",
                      "children":
                      [
                          {"name":"Notepad++"},
                          {"name":"EditPlus"},
                          {"name":"Sublime Text"}
                      ]
                  },
                  {
                      "name":"服務(wù)器軟件",
                      "children":
                      [
                          {"name":"Apache Http Server"},
                          {"name":"Tomcat"}
                      ]
                  },
                  {"name":"下載D3.js"}
              ] 
          },
          
          { 
              "name":"入門(mén)",
              "children":
              [
                  {
                      "name":"選擇集",
                      "children":
                      [
                          {"name":"select"},
                          {"name":"selectAll"}
                      ]
                  },
                  {
                      "name":"綁定數(shù)據(jù)",
                      "children":
                      [
                          {"name":"datum"},
                          {"name":"data"}
                      ]
                  },
                  {"name":"添加刪除元素"},
                  {
                      "name":"簡(jiǎn)單圖形",
                      "children":
                      [
                          {"name":"柱形圖"},
                          {"name":"折線(xiàn)圖"},
                          {"name":"散點(diǎn)圖"}
                      ]
                  },
                  {"name":"比例尺"},
                  {"name":"生成器"},
                  {"name":"過(guò)渡"}
              ] 
          },
          
          { 
              "name":"進(jìn)階" , 
              "children":
              [
                  {
                      "name":"布局的應(yīng)用",
                      "children":
                      [
                          {"name":"餅狀圖"},
                          {"name":"樹(shù)狀圖"},
                          {"name":"矩陣樹(shù)圖"}
                      ]
                  },
                  {"name":"地圖"}
              ]
          }
      ]
      }

      其次,依次創(chuàng)建樹(shù)狀圖布局、對(duì)角線(xiàn)生成器等,用于繪制樹(shù)狀圖。

      然后,實(shí)現(xiàn)最關(guān)鍵的重繪函數(shù),函數(shù)聲明如下:

      function redraw(source)

      只有一個(gè)參數(shù)source,這是被點(diǎn)擊的節(jié)點(diǎn),如果該節(jié)點(diǎn)原來(lái)為閉合狀態(tài),點(diǎn)擊后其子節(jié)點(diǎn)將顯現(xiàn),如果原來(lái)為打開(kāi)狀態(tài),點(diǎn)擊后其子節(jié)點(diǎn)將隱藏。函數(shù)體的實(shí)現(xiàn),分為四個(gè)步驟:

      2.1 調(diào)用布局,計(jì)算節(jié)點(diǎn)和連線(xiàn)數(shù)組

      樹(shù)狀圖布局的tree.nodes()返回節(jié)點(diǎn)數(shù)組,tree.links()返回連線(xiàn)數(shù)組。其中,對(duì)節(jié)點(diǎn)的y坐標(biāo)重新計(jì)算,使其只與節(jié)點(diǎn)的深度有關(guān),由于后期繪制節(jié)點(diǎn)和連線(xiàn)時(shí)要將x和y坐標(biāo)對(duì)調(diào),因此這里重計(jì)算的實(shí)際上是水平方向的坐標(biāo)。

      //應(yīng)用布局,計(jì)算節(jié)點(diǎn)和連線(xiàn)  
      var nodes = tree.nodes(root);  
      var links = tree.links(nodes);  
        
      //重新計(jì)算節(jié)點(diǎn)的y坐標(biāo)  
      nodes.forEach(function(d) { d.y = d.depth * 180; });

      之所以重新計(jì)算y坐標(biāo),是為了當(dāng)數(shù)據(jù)更新(用于點(diǎn)擊節(jié)點(diǎn))時(shí),保證樹(shù)狀圖的結(jié)構(gòu)不要發(fā)生太大的變化,如此看起來(lái)比較自然。

      2.2 分別處理節(jié)點(diǎn)的update、enter、exit三部分

      在svg里選擇當(dāng)前所有的節(jié)點(diǎn),使其與節(jié)點(diǎn)數(shù)組nodes綁定,綁定時(shí)要設(shè)定一個(gè)鍵函數(shù)。鍵函數(shù)里直接返回d.name,當(dāng)節(jié)點(diǎn)數(shù)組發(fā)生更新時(shí),新節(jié)點(diǎn)要與舊節(jié)點(diǎn)在名稱(chēng)上相對(duì)應(yīng)。

      //獲取節(jié)點(diǎn)的update部分  
      var nodeUpdate = svg.selectAll(".node")  
                     .data(nodes, function(d){ return d.name; });  
        
      //獲取節(jié)點(diǎn)的enter部分  
      var nodeEnter = nodeUpdate.enter();  
        
      //獲取節(jié)點(diǎn)的exit部分  
      var nodeExit = nodeUpdate.exit(); 

      先處理enter部分,即添加節(jié)點(diǎn)。節(jié)點(diǎn)的構(gòu)成為:分組元素里有一個(gè)圓表示節(jié)點(diǎn),還有一個(gè)文字元素表示節(jié)點(diǎn)的名稱(chēng)。元素結(jié)構(gòu)如下:

      本例中,每一個(gè)新添加的節(jié)點(diǎn)都將緩慢地過(guò)渡到自己本身的位置,如此更具有友好性。因此,新節(jié)點(diǎn)的初始位置都設(shè)定在source節(jié)點(diǎn)處,確切的說(shuō)是重回之前source節(jié)點(diǎn)的位置,該坐標(biāo)是保存在source.x0和source.y0里的。另外,對(duì)于每一個(gè)新節(jié)點(diǎn),設(shè)置的半徑為0,設(shè)置為完全透明,接下來(lái)在處理update部分的時(shí)候會(huì)將這些新節(jié)點(diǎn)過(guò)渡到正常狀態(tài)的。下圖展示了處理enter部分和update部分時(shí)如何節(jié)點(diǎn)的位置時(shí)如何確定和過(guò)渡的。

      處理enter部分的代碼如下。

      //1. 節(jié)點(diǎn)的 Enter 部分的處理辦法  
      var enterNodes = nodeEnter.append("g")  
             .attr("class","node")  
             .attr("transform", function(d) {   
      return "translate(" + source.y0 + "," + source.x0 + ")";   
      })  
             .on("click", function(d) {   
      toggle(d);   
      redraw(d);   
      });  
        
      //省略添加圓和文字部分  

      然后處理update部分,將所有節(jié)點(diǎn)(包括在enter部分新添加的節(jié)點(diǎn))都緩緩過(guò)渡到新的位置。由于新的節(jié)點(diǎn)數(shù)組是與節(jié)點(diǎn)選擇集綁定在一起的,因此d.x和d.y里保存的就是新的坐標(biāo)值。

      //2. 節(jié)點(diǎn)的 Update 部分的處理辦法  
      var updateNodes = nodeUpdate.transition()  
                     .duration(500)  
                     .attr("transform", function(d) {   
      return "translate(" + d.y + "," + d.x + ")";   
      }); 

      最后處理exit部分,需要?jiǎng)h除的節(jié)點(diǎn)的位置緩緩過(guò)渡到其父節(jié)點(diǎn)處。

      //3. 節(jié)點(diǎn)的 Exit 部分的處理辦法  
      var exitNodes = nodeExit.transition()  
       .duration(500)  
      .attr("transform", function(d) {   
      return "translate(" + source.y + "," + source.x + ")";   
      })  
       .remove();  

      2.3 分別處理連線(xiàn)的update、enter、exit三部分

      在svg中選擇所有的連線(xiàn),綁定連線(xiàn)數(shù)組links,由此可獲得連線(xiàn)的update、enter、exit部分。

      //獲取連線(xiàn)的update部分  
      var linkUpdate = svg.selectAll(".link")  
               .data(links, function(d){ return d.target.name; });  
        
      //獲取連線(xiàn)的enter部分  
      var linkEnter = linkUpdate.enter();  
        
      //獲取連線(xiàn)的exit部分  
      var linkExit = linkUpdate.exit(); 

      對(duì)于連線(xiàn)的enter部分,是插入路徑元素path,路徑由對(duì)角線(xiàn)生成器獲取,對(duì)角線(xiàn)的起點(diǎn)和終點(diǎn)坐標(biāo)都是(source.x0, source.y0)。

      對(duì)于連線(xiàn)的update部分,將所有的連線(xiàn)的位置(對(duì)角線(xiàn)的起點(diǎn)和終點(diǎn))更新到新的位置,即目前綁定的數(shù)組links里保存的位置。

      對(duì)于連線(xiàn)的exit部分,令其緩緩過(guò)渡到當(dāng)前的source點(diǎn),再移除。

      //1. 連線(xiàn)的 Enter 部分的處理辦法  
      linkEnter.insert("path",".node")  
                .attr("class", "link")  
                .attr("d", function(d) {  
                    var o = {x: source.x0, y: source.y0};  
                    return diagonal({source: o, target: o});  
                })  
                .transition()  
                .duration(500)  
                .attr("d", diagonal);  
        
       //2. 連線(xiàn)的 Update 部分的處理辦法  
       linkUpdate.transition()  
              .duration(500)  
              .attr("d", diagonal);  
        
       //3. 連線(xiàn)的 Exit 部分的處理辦法  
       linkExit.transition()  
                .duration(500)  
                .attr("d", function(d) {  
                  var o = {x: source.x, y: source.y};  
                  return diagonal({source: o, target: o});  
                })  
                .remove();  

      2.4 保存當(dāng)前的節(jié)點(diǎn)坐標(biāo)

      當(dāng)用戶(hù)點(diǎn)擊節(jié)點(diǎn)后,數(shù)據(jù)發(fā)生更新,即每個(gè)節(jié)點(diǎn)的坐標(biāo)要發(fā)生更新。但是,在對(duì)節(jié)點(diǎn)和連線(xiàn)進(jìn)行過(guò)渡操作的時(shí)候,需要使用到更新前的數(shù)據(jù)(source.x0和source.y0)。因此,每一次調(diào)用重繪函數(shù),都要將當(dāng)前節(jié)點(diǎn)的位置保存下來(lái)。

      nodes.forEach(function(d) {  
            d.x0 = d.x;  
            d.y0 = d.y;  
      }); 

      x和y坐標(biāo)分別保存在x0和y0中,在調(diào)用redraw(source)時(shí),被點(diǎn)擊的節(jié)點(diǎn)被作為參數(shù)傳到了重繪函數(shù)里,因此source.x0和source.y0里保存的是被點(diǎn)擊之前節(jié)點(diǎn)的坐標(biāo)。

      3. 結(jié)果

      結(jié)果如下圖所示,點(diǎn)擊節(jié)點(diǎn)可以展開(kāi)子節(jié)點(diǎn)。

      源代碼請(qǐng)單擊以下鏈接,郵件查看源代碼:

      http://www./demo/G-10.0/mind.html

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀(guān)點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶(hù) 評(píng)論公約

        類(lèi)似文章 更多