D3默認的樹狀圖畫圖使用的是svg, 好比這個來自D3做者的例子:javascript
https://bl.ocks.org/mbostock/...java
使用svg有好有壞:node
在大多數數據量不是特別大狀況下, 使用svg的好處是遠遠蓋過壞處的,但若是咱們真的須要渲染大量的數據呢?git
https://github.com/ssthouse/o...github
https://ssthouse.github.io/or...npm
上面的demo就是使用 D3.js + Canvas 的方式實現的, 在組織的層數超過300時纔會出現明顯的卡頓, 能知足大部分的組織結構圖的數據.canvas
虛擬Dom
中畫好圖像虛擬Dom
中的數據 (座標 & 線的path) 等繪製到Canvas上使用 Unique-color
的方式實現Canvas 的用戶交互bash
虛擬Dom
中畫好圖像首先調使用D3建立 Tree的虛擬Dom:微信
this.data = this.d3.hierarchy(data) this.treeGenerator = this.d3.tree() .nodeSize([this.nodeWidth, this.nodeHeight]) let nodes = this.treeData.descendants() let links = this.treeData.links()
上面的變量 nodes
和 links
如今就包含告終構圖中每一個 組織節點
和 鏈接線
的座標信息.dom
虛擬Dom
中的數據 (座標 & 線的path) 等繪製到Canvas上在 drawShowCanvas中, 經過 d3.select拿到虛擬的dom節點, 再使用 Canvas的繪圖函數進行繪製, 這裏用到了一些 Util的工具方法, 具體實現請參考源碼.
drawShowCanvas () { this.context.clearRect(-50000, -10000, 100000, 100000) let self = this // draw links this.virtualContainerNode.selectAll('.link') .each(function () { let node = self.d3.select(this) let linkPath = self.d3.linkVertical() .x(function (d) { return d.x }) .y(function (d) { return d.y }) .source(function () { return {x: node.attr('sourceX'), y: node.attr('sourceY')} }) .target(function () { return {x: node.attr('targetX'), y: node.attr('targetY')} }) let path = new Path2D(linkPath()) self.context.stroke(path) }) this.virtualContainerNode.selectAll('.orgUnit') .each(function () { let node = self.d3.select(this) let treeNode = node.data()[0] let data = treeNode.data self.context.fillStyle = '#3ca0ff' let indexX = Number(node.attr('x')) - self.unitWidth / 2 let indexY = Number(node.attr('y')) - self.unitHeight / 2 // draw unit outline rect (if you want to modify this line ===> please modify the same line in `drawHiddenCanvas`) Util.roundRect(self.context, indexX, indexY, self.unitWidth, self.unitHeight, 4, true, false) Util.text(self.context, data.name, indexX + self.unitPadding, indexY + self.unitPadding, '20px', '#ffffff') // Util.text(self.context, data.title, indexX + self.unitPadding, indexY + self.unitPadding + 30, '20px', '#000000') let maxWidth = self.unitWidth - 2 * self.unitPadding Util.wrapText(self.context, data.title, indexX + self.unitPadding, indexY + self.unitPadding + 24, maxWidth, 20) }) }
Unique-color
的方式實現Canvas 的用戶交互下圖中能夠看到, 其實是有兩張Canvas的, 其中下面的Canvas除了的節點顏色不一樣外, 和上面的Cavans繪製的數據徹底相同.
drawCanvas () { this.drawShowCanvas() this.drawHiddenCanvas() }
在上面一張Canvas上監聽用戶點擊事件, 經過象素的座標, 在下面一張圖中拿到用戶點擊的節點 (注意: 顏色和節點的鍵值對 是在下面一張Canvas繪製的時候就已經建立好的.)
setClickListener () { let self = this this.canvasNode.node().addEventListener('click', function (e) { let colorStr = Util.getColorStrFromCanvas(self.hiddenContext, e.layerX, e.layerY) let node = self.colorNodeMap[colorStr] if (node) { // let treeNodeData = node.data()[0] // self.hideChildren(treeNodeData, true) self.toggleTreeNode(node.data()[0]) self.update(node.data()[0]) } }) }
下面是建立 unique-color和節點的 鍵值對 的參考代碼:
addColorKey () { // give each node a unique color let self = this this.virtualContainerNode.selectAll('.orgUnit') .each(function () { let node = self.d3.select(this) let newColor = Util.randomColor() while (self.colorNodeMap[newColor]) { newColor = Util.randomColor() } node.attr('colorKey', newColor) node.data()[0]['colorKey'] = newColor self.colorNodeMap[newColor] = node }) }
please replace the data in /src/base/data-generator
with your own nested data.
please add your data drawing logic in /src/components/org-chart.js #drawShowCanvas
if you like it , welcome to star and fork
https://github.com/ssthouse/o...
# install dependencies npm install # serve with hot reload at localhost npm run dev # build for production with minification (build to ./docs folder, which can be auto servered by github page 🤓) npm run build
這裏是個人 D3.js 、 數據可視化 的github 地址, 歡迎 start & fork
郵箱: ssthouse@163.com
微信: