d3.js 生成 svg tree 樹形結構圖

本文實例 demo http://www.jq22.com/jquery-info21370 

d3中文版    https://d3js.org.cn
官方API   https://github.com/d3/d3/blob/master/API.md
官方API v3  https://github.com/d3/d3-3.x-api-reference/blob/master/API-Reference.md
官方示例: https://github.com/d3/d3/wiki/Gallery

可搜索示例:https://blockbuilder.org/search?d3version=v4&text=tree

 

w3cschool 字典 https://www.w3cschool.cn/dict/

 d3 v4  https://www.w3cschool.cn/doc_d3_4/dict

 d3 v3  https://www.w3cschool.cn/doc_d3_3/dict

 

使用D3和FileReader在線生成樹形圖  https://blog.csdn.net/u014291497/article/details/70991125

 

d3.v3 和 d3.v4 的變換 中英文版

https://github.com/d3/d3/blob/master/CHANGES.md#hierarchies-d3-hierarchy

https://github.com/xswei/d3js_doc/blob/master/API_Reference/CHANGES.md


本人d3 只製作了tree樹狀圖,別的不太瞭解,調接口的沒有總結,下面這個 demo 是原生的,3.x

d3 需要英語好,纔好入門。

echarts 中有樹狀圖 可以先參考一下。
https://echarts.baidu.com/examples/editor.html?c=tree-basic

這邊有一個別人的在線demo https://codepen.io/zhulinpinyu/pen/EaZrmM/?editors=0010

雙向樹狀圖 線是直線 

 http://bl.ocks.org/jdarling/2503502 

d3.js  中文版 3.x 轉自別人的翻譯

http://blog.mn886.net/chenjianhua/show/773c07b3abce/index.html

D3.js拖放,Zoomable,平移,可摺疊樹與自動調整大小。在線demo

http://bl.ocks.org/robschmuecker/7880033

 

收藏轉自: 有重複,以供參考

https://www.cnblogs.com/wanyong-wy/p/7603123.html

 https://www.cnblogs.com/quyixuanblog/p/5754129.html

 https://blog.csdn.net/songqiang2011/article/details/86521194

 https://blog.csdn.net/lzhlzz/article/details/38561737

https://www.jianshu.com/p/909a241acfdd 

https://www.jianshu.com/p/6ef01845854f

https://blog.csdn.net/loveljy_19901114/article/details/81530222

http://www.cleey.com/blog/single/id/705.html

https://www.cnblogs.com/shihuc/p/6150526.html

 

https://godbasin.github.io/2018/03/10/d3-tree-notes-8-add-path-click/

http://d3.godbasin.com/8-add-path-click/index.html

 

http://www.decembercafe.org

域名過期 // http://www.ourd3js.com/wordpress/865/#more-865

 

snap.svg  轉自張鑫旭  可以參考一下
https://www.zhangxinxu.com/GitHub/demo-Snap.svg/demo/basic/Snap.hsl.php

 

設置線

function line_1 (d) { // 直線
        // 需要兩個參數,分別是一個點的x軸和y軸座標,L命令將會在當前位置和新位置(L前面畫筆所在的點)之間畫一條線段。
        let x0 = d.source.x,
          y0 = d.source.y,
          x1 = d.target.x,
          y1 = d.target.y;
        return `M ${x0},${y0} L ${x1},${y1}`;
      }

      function line_2 (d) { // 弧線
        let x0 = d.source.x,
          y0 = d.source.y,
          x1 = d.target.x,
          y1 = d.target.y;

        let dx = x1 - x0,
          dy = y1 - y0,
          dr = Math.sqrt(dx * dx + dy * dy);
        return `M ${x0},${y0} A ${dr},${dr},0,0,1 ${x1},${y1}`;
      }

      function line_3 (d) { // 垂直對角線
        // 三次貝塞爾曲線需要定義一個點和兩個控制點,所以用C命令創建三次貝塞爾曲線,
        // 需要設置三組座標參數:C x1 y1, x2 y2, x y (or c dx1 dy1, dx2 dy2, dx dy),
        // 最後一個座標(x,y)表示的是曲線的終點,另外兩個座標是控制點,(x1,y1)是起點的控制點,(x2,y2)是終點的控制點。
        let x0 = d.source.x,
          y0 = d.source.y,
          x1 = d.target.x,
          y1 = d.target.y;

        let r = y1 - y0;
        return `M ${x0},${y0} C ${x0},${y0 + r / 2} ${x1},${y0 + r / 2} ${x1},${y1}`;
      }

      function line_4 (d) { // 水平對角線
        let x0 = d.source.x,
          y0 = d.source.y,
          x1 = d.target.x,
          y1 = d.target.y;

        let r = x1 - x0;
        return `M ${x0},${y0} C ${x0 + r / 2},${y0} ${x0 + r / 2},${y1} ${x1},${y1}`;
      }

      function line_5 (d) { // 水平折線
        let x0 = d.source.x,
          y0 = d.source.y,
          x1 = d.target.x,
          y1 = d.target.y;

        let dx = x1 - x0,
          dy = y1 - y0;

        return `M ${x0},${y0} H ${dx / 2}, V ${dy} H ${x1}`;
      }

      function line_6 (d) { // 垂直折線
        let x0 = d.source.x,
          y0 = d.source.y,
          x1 = d.target.x,
          y1 = d.target.y;

        let dx = x1 - x0,
          dy = y1 - y0;

        return `M ${x0},${y0} V ${dy / 2}, H ${dx} V ${x1}`;
      }

      function line_7 () { // 徑向對角線是基於垂直對角線製作
        let arc0 = 0, r0 = 0,
          arc1 = 45 * Math.PI / 180, r1 = 100;

        let x0 = r0 * Math.cos(arc0),
          y0 = r0 * Math.sin(arc0),
          x1 = r1 * Math.cos(arc1),
          y1 = r1 * Math.sin(arc1);

        return line_3({
          "source": { "x": x0, "y": y0 },
          "target": { "x": x1, "y": y1 }
        })
      }

 

<!DOCTYPE html>
<!--suppress ALL -->
<html>
<head>
	<meta charset="utf-8">
	<title>3</title>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
	<script src="https://d3js.org/d3.v3.min.js"></script>

	<style>

		body, html {
			width: 100%;
			height: 100%;
			margin: 0;
			box-sizing: border-box;
			overflow: hidden;
		}

		    .node {
			cursor: pointer;
		}

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

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

		.link {
			fill: none;
			stroke: #ccc;
			stroke-width: 1.5px;
		}

		    .link {
			fill: none;
			stroke: #ccc;
			stroke-width: 1.5px;
		}

		.tree {
			height: 100%;
			margin: 0 auto;
			background: #E0E0E0;
			box-sizing: border-box;
		}

		.tree svg {
			width: 100%;
			height: 100%;
		}
		.chartTooltip {
			position: absolute;
			width: 200px;
			height: auto;
			padding: 10px;
			box-sizing: border-box;
			background-color: white;
			border-radius: 5px;
			box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4);

		}
		.chartTooltip.hidden {
			display: none;
		}
		.chartTooltip p {
			margin: 0;
			font-size: 14px;
			line-height: 20px;
			word-wrap: break-word;
		}
	</style>
</head>
<body>

<div class="chartTooltip hidden">
	<p>
		<strong class="name"></strong>
	</p>
</div>

<div class="tree" id="tree">

</div>

<script type="text/javascript">
  //位置參數
  tree();
  function tree(){
	var width = document.getElementById("tree").offsetWidth;
	var height = document.getElementById("tree").offsetHeight;
	var i = 0,
		duration = 750,
		root;

	var tree = d3.layout.tree().size([height, width])
	// tree.nodeSize([50,0]) // 子節點之間的距離
	var diagonal = d3.svg.diagonal() .projection(function (d) { return [d.y, d.x]; });
	var zoom = d3.behavior.zoom().scaleExtent([0.1, 100]).on("zoom", zoomed);//添加放大縮小事件

	var svg = d3.select("body").select("#tree").append("svg")
		.call(zoom)//給svg綁定zoom事件
		.append("g")
		.attr("transform", "translate(150, 0)")

	zoom.translate([150,0])

	function zoomed() {
	  svg.attr("transform",
		  "translate(" + d3.event.translate + ")" +
		  "scale(" + d3.event.scale + ")"
	  );
	}

	d3.json("tree2.json", function (error, flare) {
	  // 根節點和位置
	  root = flare;
	  root.x0 = height / 2;
	  root.y0 = 0;

	  //(1) 摺疊函數,遞歸調用,有子孫的就把children(顯示)給_children(不顯示)暫存,便於摺疊,
	  function collapse(d) {
		if (d.children) {
		  d._children = d.children;
		  d._children.forEach(collapse);
		  d.children = null;
		}
	  }

	  // 摺疊根節點的每個孩子
	  root.children.forEach(collapse);

	  // 	console.log(root) // 打印一箇中國
	  // 摺疊之後要重繪
	  update(root);
	  //(2) 更新佈局
	  function update(source) {
		// console.log(source)//  獲取到中國 children 8個
		// (2-1) 計算新樹的佈局
		var nodes = tree.nodes(root).reverse(),
			links = tree.links(nodes);

		// (2-2) 樹的深度這裏樹d.y。樹的寬度最大720,要分四層,所以每層就乘180
		nodes.forEach(function (d) {
		  d.y = d.depth * 180;// 樹的x,y倒置了,所以這裏Y其實是橫向的
		  // console.log(d );  // 獲取到 展示的所有 中國浙江等
		});


		// (2-3) 數據連接,根據id綁定數據
		var node = svg.selectAll("g.node")
			.data(nodes, function (d) {
			  return d.id //最初新點開的節點都沒有id
				  || (d.id = ++i); //爲沒有id的節點添加上ID
			});


		var div = d3.select('.chartTooltip').style("opacity", 0)
		var timer = null;

		var nodeEnter = node.enter().append("g")
			.attr("class", "node")
			.style("cursor","pointer")
			.attr("transform", function (d) {
			  return "translate(" + source.y0 + "," + source.x0 + ")"
			})
			.on("mouseover", d => {
			  if(timer) clearTimeout(timer)
			  d3.select('.chartTooltip').transition().duration(300).style('opacity', 1).style('display', 'block')
			  // 從d3.event獲取鼠標的位置
			  var transform = d3.event;
			  var yPosition = transform.offsetY + 20;
			  var xPosition = transform.offsetX + 20;
			  // 將浮層位置設置爲鼠標位置
			  var chartTooltip = d3
				  .select(".chartTooltip")
				  .style("left", xPosition + "px")
				  .style("top", yPosition + "px");
			  // 更新浮層內容
			  chartTooltip.select(".name").text(d.name);
			  // 移除浮層hidden樣式,展示浮層
			  chartTooltip.classed("hidden", false);
			})
			// 添加mouseover事件
			.on("mouseout", () => {
			  // 添加浮層hidden樣式,隱藏浮層
			  timer = setTimeout(function(){
				d3.select('.chartTooltip').style('opacity', 0).style('display', 'none')
				// d3.select('.chartTooltip').style('opacity', 0)
			  },400);
			})
			.on("click", function(d) {
			  click(d)
			});
		d3.select('.chartTooltip').on('mouseover', function() {
		  if(timer) clearTimeout(timer)
		  d3.select('.chartTooltip').transition().duration(300).style('opacity', 1).style('display', 'block')
		}).on('mouseout', function() {
		  timer = setTimeout(function(){
			d3.select('.chartTooltip').style('opacity', 0).style('display', 'none')
		  },400);
		})


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

		//文字 1
		nodeEnter.append("text")
			.attr("x", function (d) {
			  return d.children || d._children ? 50 : 30;
			})
			.attr("dy", ".35em")
			.attr("text-anchor", function (d) {
			  return d.children || d._children ? "end" : "start";
			})
			.text(function (d) {
			  return d.name;
			})
			.style("fill-opacity", 1)
			.style("font-size", "12px");

		//文字 1
		nodeEnter.append("text")
			.attr("x", function (d) {
			  return d.children || d._children ? 20 : 0;
			})
			.attr("y", function (d) {
			  return d.children || d._children ? 25 : 20;
			})
			.attr("dy", ".35em")
			.attr("text-anchor", function (d) {
			  return d.children || d._children ? "end" : "start";
			})
			.text(function (d) {
			  return d.name
			})
			.style("fill-opacity", 1)
			.style("font-size", "12px");


		// (2-5) 原有節點更新到新位置
		var nodeUpdate = node.transition()
			.duration(duration)
			.attr("transform", function (d) {
			  return "translate(" + d.y + "," + d.x + ")";
			});

		nodeUpdate.select("circle")
			.attr("r", 4.5)
			.attr("r", function (d) {
			  return d._children ? "5" : "5";
			})
			.style("fill", function (d) {
			  return d._children ? "#f00" : "#f90";
			});
		nodeUpdate.select("text")
			.style("fill-opacity", 1);

		// (2-6) 摺疊節點的子節點收縮回來
		var nodeExit = node.exit().transition()
			.duration(duration)
			.attr("transform", function (d) {
			  return "translate(" + source.y + "," + source.x + ")";
			})
			.remove();
		nodeExit.select("circle")
			.attr("r", 1e-6);
		nodeExit.select("text")
			.style("fill-opacity", 1);

		// (2-7) 數據連接,根據目標節點的id綁定數據
		var link = svg.selectAll("path.link")
			.data(links, function (d) {
			  return d.target.id;
			});

		// (2-8) 增加新連接
		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});
			});

		// (2-9) 原有連接更新位置
		link.transition()
			.duration(duration)
			.attr("d", diagonal);

		// (2-10) 摺疊的鏈接,收縮到源節點處
		link.exit().transition()
			.duration(duration)
			.attr("d", function (d) {
			  var o = {x: source.x, y: source.y};
			  return diagonal({source: o, target: o});
			})
			.remove();
		// 把舊位置存下來,用以過渡
		nodes.forEach(function (d) {
		  d.x0 = d.x;
		  d.y0 = d.y;
		});
	  }

	  // (3) 切換摺疊與否
	  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>

 

{
  "name":"中國",
  "children":
  [

    {
      "name":"廣西" ,
      "children":
      [
        {
          "name":"桂林"

        },
        {"name":"南寧"},
        {"name":"柳州"},
        {"name":"防城港"},
        {
          "name":"浙江"
        }
      ]
    },
    {
      "name":"浙江4" ,
      "children":
      [
        {"name":"杭州",
          "children":[
            {"name":"寧波",
              "children":[
                {"name":"寧波",
                  "children":[
                    {"name":"溫州",
                      "children":[
                        {"name":"溫州",
                          "children":[
                            {"name":"溫州",
                              "children":[
                                {"name":"寧波"
                                },
                                {"name":"溫州" },
                                {"name":"紹興"}
                              ]
                            },
                            {"name":"紹興"}
                          ]
                        },
                        {"name":"紹興"}
                      ]
                    },
                    {"name":"紹興"}
                  ]
                },
                {"name":"溫州"},
                {"name":"紹興"}
              ]
            },
            {"name":"溫州" }
          ]
        },
        {"name":"寧波" },
        {"name":"溫州" },
        {"name":"紹興" }
      ]
    },
    {
      "name":"浙江5" ,
      "children":
      [
        {"name":"杭州",
          "children":[
            {"name":"寧波",
              "children":[
                {"name":"寧波",
                  "children":[
                    {"name":"溫州",
                      "children":[
                        {"name":"溫州",
                          "children":[
                            {"name":"溫州",
                              "children":[
                                {"name":"寧波"
                                },
                                {"name":"溫州"},
                                {"name":"紹興"}
                              ]
                            },
                            {"name":"紹興"}
                          ]
                        },
                        {"name":"紹興"}
                      ]
                    },
                    {"name":"紹興"}
                  ]
                },
                {"name":"溫州"},
                {"name":"紹興"}
              ]
            },
            {"name":"溫州" }
          ]
        },
        {"name":"寧波"},
        {"name":"溫州" },
        {"name":"紹興" }
      ]
    },

    {
      "name":"新疆6" ,
      "children":
      [
        {"name":"烏魯木齊"},
        {"name":"克拉瑪依"},
        {"name":"吐魯番"},
        {"name":"哈密"}
      ]
    }
  ]
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章