d3 樹狀布局tree

樹狀布局Tree-Layout簡介

顯而易見,樹狀布局就是我們通常所理解的數(shù)據(jù)結(jié)構(gòu)中的樹,由一個根節(jié)點(diǎn)向外展開,與前面力導(dǎo)向圖的數(shù)據(jù)結(jié)構(gòu)模型其實(shí)是一致的,只是展示形式略有差別,用樹狀結(jié)構(gòu)展示看起來更加簡潔清晰一點(diǎn)。

API

樹結(jié)構(gòu)

  • d3.layout.tree()

    使用默認(rèn)設(shè)置創(chuàng)建一個新的樹狀布局:

  • tree.nodes(root)

    根據(jù)傳入數(shù)據(jù)計算樹的布局返回一組節(jié)點(diǎn)數(shù)組

  • tree.links(nodes)

    根據(jù)節(jié)點(diǎn)數(shù)組返回節(jié)點(diǎn)之間的父子連接關(guān)系

  • tree.separation([separation]):

    設(shè)置或取得兩個節(jié)點(diǎn)之間的間距

  • tree.size([size])

    指定樹布局的尺寸一個

svg形狀

因?yàn)樾枰獙?shí)現(xiàn)一個輻射布局,并且連線是貝塞爾曲線,需要用到坐標(biāo)投影來實(shí)現(xiàn)路徑形狀生成。
路徑形狀有很多種選擇,比如d3.svg.line()、d3.svg.area()、d3.svg.diagonal()等等。這里選用d3.svg.diagonal(),它能利用projection的API來生成svg中的path形狀的路徑。

  • d3.svg.diagonal.radial().projection()

示例代碼

根據(jù)官網(wǎng)一些人提供的Demo修改來寫了一個輻射布局。里面做了一些注釋,但仍然有一些疑惑點(diǎn),比如d.x-90那里的原因還是比較模糊。

下面是完整demo代碼

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>tree layout</title>
    <style>
        .node {
          cursor: pointer;
        }

        .node circle {
          fill: #fff;
          stroke: steelblue;
          stroke-width: 1.5px;
        }

        .node text {
          font: 10px sans-serif;
        }

        .link {
          fill: none;
          stroke: #ccc;
          stroke-width: 1.5px;
        }
  </style>
</head>
<body>
<script src="http://cdn.bootcss.com/d3/3.2.2/d3.v3.min.js"></script>
<script>

    var width = 800,
        height = 800;

    var i = 0,
        duration = 6000,
        root;

    var tree = d3.layout.tree()
        .size([360, 320])  //360代表展開的最大角度也就是圓,后面的r值代表整個輻射布局的展開最大半徑,并不是指某一層級的半徑而是整個樹,超過這個范圍的就不顯示了,后面85行d.y的才是某一層級的半徑設(shè)置。
        .separation(function(a, b) {
            return (a.parent == b.parent ? 1 : 2) / a.depth;  //適應(yīng)radial布局,可以簡單的理解為深度越高,相鄰子節(jié)點(diǎn)之間的距離越小。
        });

    var diagonal = d3.svg.diagonal.radial()
        .projection(function(d) { return [d.y, d.x / 180 * Math.PI]; }); //d.y代表半徑,(d.x/180)*PI就是各個節(jié)點(diǎn)的弧度表示。

    var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

    d3.json("flare1.json", function(error, flare) {
      if (error) throw error;

      root = flare;
      root.x0 = height / 2;  //由于是輻射布局這邊當(dāng)y0設(shè)置為0,那么這個角度x0隨便設(shè)置也無所謂 因?yàn)榭隙ㄊ窃趫A心。這里的理解不知道對不對
      root.y0 = 0;

      function collapse(d) {
        if (d.children) {
          d._children = d.children;
          d._children.forEach(collapse);
          d.children = null;
        }
      }

      root.children.forEach(collapse);
      update(root);
    });

    // // Hack to make this example display correctly in an iframe on bl.ocks.org 為了在iframe中正常顯示而已。
    d3.select(self.frameElement).style("height", "800px");

    function update(source) {

      // Compute the new tree layout.
      var nodes = tree.nodes(root),
          links = tree.links(nodes);

      // Normalize for fixed-depth. 根據(jù)深度來給定相應(yīng)半徑,也就是深度越大半徑越長。
      nodes.forEach(function(d) { d.y = d.depth * 180; });

      // Update the nodes…
      var node = svg.selectAll("g.node")
          .data(nodes, function(d) { return d.id || (d.id = ++i); });

      // Enter any new nodes at the parent's previous position.
      var nodeEnter = node.enter().append("g")
          .attr("class", "node")
          // .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
          .on("click", click);

      nodeEnter.append("circle")
          .attr("r", 1e-6)
          .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

      nodeEnter.append("text")
          // .attr("x", function(d) { return d.children || d._children ? -10 : 10; })
          .attr("dy", ".35em")

      .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })//小于180度的文字放在前面,否則放在后面  
      .attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; })
          // .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
          .text(function(d) { return d.name; })
          .style("fill-opacity", 1e-6);

      // Transition nodes to their new position.
      var nodeUpdate = node.transition()
          .duration(duration)
          .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
          //translate 與 rotate 的作用效果是怎么樣的???
      nodeUpdate.select("circle")
          .attr("r", 4.5)
          .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

      nodeUpdate.select("text")
          .style("fill-opacity", 1);

      // Transition exiting nodes to the parent's new position.
      var nodeExit = node.exit().transition()
          .duration(duration)
          .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
          .remove();

      nodeExit.select("circle")
          .attr("r", 1e-6);

      nodeExit.select("text")
          .style("fill-opacity", 1e-6);

      // Update the links…
      var link = svg.selectAll("path.link")
          .data(links, function(d) { return d.target.id; });

      // Enter any new links at the parent's previous position.
      link.enter().insert("path", "g")
          .attr("class", "link")
          .attr("d", function(d) {
            var o = {x: source.x0, y: source.y0};
            return diagonal({source: o, target: o});
          });

      // Transition links to their new position.
      link.transition()
          .duration(duration)
          .attr("d", diagonal);

      // Transition exiting nodes to the parent's new position.
      link.exit().transition()
          .duration(duration)
          .attr("d", function(d) {
            var o = {x: source.x0, y: source.y0};
            return diagonal({source: o, target: o});
          })
          .remove();

      // Stash the old positions for transition.
      nodes.forEach(function(d) {
        d.x0 = d.x;
        d.y0 = d.y;
      });
    }

    // Toggle children on click.
    function click(d) {
      if (d.children) {
        d._children = d.children;
        d.children = null;
      } else {
        d.children = d._children;
        d._children = null;
      }
      update(d);
    }

  </script>
</body>
</html>

json數(shù)據(jù)文件flare1.json如下

{
 "name": "flare",
 "children": [
  {
   "name": "analytics",
   "children": [
    {
     "name": "cluster",
     "children": [
      {"name": "AgglomerativeCluster", "size": 3938},
      {"name": "CommunityStructure", "size": 3812},
      {"name": "HierarchicalCluster", "size": 6714},
      {"name": "MergeEdge", "size": 743}
     ]
    },
    {
     "name": "graph",
     "children": [
      {"name": "BetweennessCentrality", "size": 3534},
      {"name": "LinkDistance", "size": 5731},
      {"name": "MaxFlowMinCut", "size": 7840},
      {"name": "ShortestPaths", "size": 5914},
      {"name": "SpanningTree", "size": 3416}
     ]
    },
    {
     "name": "optimization",
     "children": [
      {"name": "AspectRatioBanker", "size": 7074}
     ]
    }
   ]
  },
  {
   "name": "animate",
   "children": [
    {"name": "Easing", "size": 17010},
    {"name": "FunctionSequence", "size": 5842},
    {
     "name": "interpolate",
     "children": [
      {"name": "ArrayInterpolator", "size": 1983},
      {"name": "ColorInterpolator", "size": 2047},
      {"name": "DateInterpolator", "size": 1375},
      {"name": "Interpolator", "size": 8746},
      {"name": "MatrixInterpolator", "size": 2202},
      {"name": "NumberInterpolator", "size": 1382},
      {"name": "ObjectInterpolator", "size": 1629},
      {"name": "PointInterpolator", "size": 1675},
      {"name": "RectangleInterpolator", "size": 2042}
     ]
    },
    {"name": "ISchedulable", "size": 1041},
    {"name": "Parallel", "size": 5176},
    {"name": "Pause", "size": 449},
    {"name": "Scheduler", "size": 5593},
    {"name": "Sequence", "size": 5534},
    {"name": "Transition", "size": 9201},
    {"name": "Transitioner", "size": 19975},
    {"name": "TransitionEvent", "size": 1116},
    {"name": "Tween", "size": 6006}
   ]
  },
  {
   "name": "data",
   "children": [
    {
     "name": "converters",
     "children": [
      {"name": "Converters", "size": 721},
      {"name": "DelimitedTextConverter", "size": 4294},
      {"name": "GraphMLConverter", "size": 9800},
      {"name": "IDataConverter", "size": 1314},
      {"name": "JSONConverter", "size": 2220}
     ]
    },
    {"name": "DataField", "size": 1759},
    {"name": "DataSchema", "size": 2165},
    {"name": "DataSet", "size": 586},
    {"name": "DataSource", "size": 3331},
    {"name": "DataTable", "size": 772},
    {"name": "DataUtil", "size": 3322}
   ]
  }
 ]
}

參考資料

1.官方手冊

2.Collapsible Tree

3.Radial Tree

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內(nèi)容

  • 2016-04-14 本篇文章談?wù)?d3 的動畫、svg 圖形生成器以及 d3 提供的 layout 一、動畫 1...
    HeyDelilah閱讀 1,711評論 0 2
  • 本節(jié)內(nèi)容將描述餅狀圖、力導(dǎo)向圖、弦圖、集群圖、樹狀圖、打包圖、分區(qū)圖、圓形分區(qū)圖、直方圖、捆圖、堆棧圖、矩陣樹圖、...
    笨笨的笨小孩閱讀 6,551評論 0 2
  • 對集合的操作 關(guān)于d3.attr 一個可以處理很多情況的函數(shù),當(dāng)只傳入一個參數(shù)時,如果是string,則返回該屬性...
    陳堅生閱讀 2,552評論 0 2
  • 建立深厚友誼和人際關(guān)系與周圍的人培養(yǎng)良好的友誼,你應(yīng)該遵循7個做法: 1全然接納他們原來的樣子 ──而不是判斷、...
    再回首_心依舊閱讀 176評論 0 1
  • 1.寫作業(yè) 2.做卷子 3.聽視頻課
    千丈暗香閱讀 182評論 0 0