相對於echart, highchart等其餘圖表庫算是一個比較底層的可視化工具,簡單來說他不提供任何一種現成的圖表,全部的圖表都是咱們在它的庫裏挑選合適的方法構建而成。前端
基於上面的理解,d3無疑會複雜不少可是也強大自由的多,另外由於d3基於svg因此修改圖表的樣式和結構也會方便不少,可是一樣是這個緣由,d3的性能比canvas類庫差了很多,dom畢竟是拖累瀏覽器性能的罪魁禍首。順口提一句,d3也是能夠基於canvas構建圖表的。可是這篇文章就不提了。node
對於d3咱們能夠簡單的將其分個類:數據處理, dom處理,事件以及其餘。 其實dom和事件其實能夠合到一塊兒。react
前端作可視化的時候確定須要對數據進行處理,d3提供了一下經常使用的方法。ajax
由於d3是基於svg因此跟dom打交道確定是必須的,這裏必定程度替代了jQuery之類的功能。算法
事件的話其實就是一些交互好比滾輪,拖拽等等都是基礎功能能夠進行一系列組合排序canvas
請求就是ajax請求數據源了。後端
數據處理就很簡單了,就是對於數組和集合以及時間的一些處理方法, 好比數組求中位數方差等等,和lodash的一些方法有重合,可是仍是偏向數學方面,方法有點多這裏不一一列出了:api
// array的方法
d3.min([1, 2, 3, 4]) // 1 不一樣於Math,min忽略NaN undefined等
d3.range(1, 10) // [1, 2 ... 10]
// collection的方法
d3.entries({foo: 42, bar: true}); // [{key: "foo", value: 42}, {key: "bar", value: true}]
var map = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
map.get("foo"); // {"name": "foo"}
map.get("bar"); // {"name": "bar"}
map.get("baz"); // undefined
// time的方法
d3.timeDays(new Date('2014-01-11'), new Date('2014-02-12')) // 獲取2014-01-11 到2014-02-12的日期數組
複製代碼
上面是單純的數據處理也就是工具類,可是d3的強大不只僅在於此,d3提供了一個強大算法庫,好比力導向圖的碰撞檢測以及tick等等,這裏的功能也屬於數據處理可是又跟插入dom密不可分。數組
d3的數據不只僅是這些有些跟dom耦合極深沒辦法徹底拎出來講, 並且d3的api極多, 這些東西不少時候也只能邊看文檔邊作。好在d3的示例不少,基本需求都能知足。瀏覽器
關於dom操做d3也提供了一系列方便的接口,好比d3.select
,d3.append
等等, 這部分的接口至關多,我的也無法一一說明, 只能說用法都是同樣的,和jQuery至關相似:
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 2.5);
複製代碼
上面的代碼是把circle
跟data
進行數據綁定並插入對應的dom節點(引用自連接):
- 首先,
svg.selectAll("circle")
返回一個空選集,由於當前 SVG 尚未任何子元素,該選集的父節點是這個 SVG 容器。- 而後將該選集與數據綁定,產生三個新的子選集,分別表明三種可能的狀態:enter、update 和 exit。因爲當前選集爲空,因此 update 和 exit 子選集也爲空,enter 子選集就包含了每條數據對應的元素的佔位符。
- update 子選集直接經過
selection.data
返回,enter 和 exit 子選集分別經過selection.enter
和selection.exit
返回。- 那些缺乏的元素經過對 enter 子選集調用
selection.append
方法來添加到 SVG 中,這樣就爲每條數據添加了一個新的圓點到 SVG 中。
如上都是鏈式操做
不一樣於canvas這裏能夠直接觸發原生事件,讓人親切不少。
事件是指基於dom的一些交互操做,包括但不限於click
等原生事件,相似jQuery,事件是經過on
進行綁定的:
selection.on('click', function (d) {}) // this指向事件元素, d是綁定的數據能夠直接使用
複製代碼
同時,d3提供了不少自定義事件諸如drag, zoom,brush等等,這時候就是經過call
調用了:
const brush = d3.brushX()
.extent([[50, 50], [1100, 150]])
.on('start brush', brushed)
.on('end', brushended)
svg.append("g")
.call(brush)
複製代碼
上面是調用brush事件,同時調用相應的回調, 都是字面意思,至於還有不少有意思的事件,都隱藏在文檔中。
這個其餘
就包含了不少東西, 好比異步請求,解析excel,動畫等等,這裏不一一說明了, 可是若是發現有需求無法實現不妨看看文檔,說不定就內置了呢。
下面給個示例, 簡單力導向圖示例jsfiddle:
核心代碼以下:
const height = 200
const width = 200
const svg = d3.select('body').append('svg')
const graph = {
nodes: [
{ id: 1, name: 'test1' },
{ id: 2, name: 'test2' }
],
links: [
{ source: 1, target: 2 }
]
}
const simulation = d3.forceSimulation()
.force('charge', d3.forceManyBody().strength(-700).distanceMin(100).distanceMax(1000))
.force('link', d3.forceLink().id(d => d.id))
.force('center', d3.forceCenter(width / 2, height / 2))
const link = svg.selectAll('link')
.data(graph.links)
.enter()
.append('line')
.attr('class', 'link')
const node = svg.selectAll('node')
.data(graph.nodes)
.enter().append('g')
.attr('class', 'node')
node.append('circle')
.attr('r', 13)
.attr('fill', '#999')
node.append('text')
.attr('dx', -18)
.attr('dy', 8)
.style('font-family', 'overwatch')
.style('font-size', '18px')
.text(d => d.name)
const ticked = function () {
link.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y);
node.attr('transform', d => `translate(${d.x}, ${d.y})`)
}
const { nodes, links } = graph
simulation.nodes(nodes).on('tick', ticked)
simulation.force('link').links(links)
複製代碼
下面簡單解析一下代碼部分,const svg = d3.select('body').append('svg')
就是上面提到的d3操做dom的部分,就是相似jQuery的插入操做, 總之咱們獲取到了svg畫布, graph
是提供了數據關係模型,可是通常來說後端不會這麼提供嚴格的對應關係, 這時候就須要咱們隊數據進行處理以獲取合理的數據格式, 通常來說數據格式都是如上。
力導向圖的核心是forceSimulation
, 如字面上的意思就是來模擬力的,這是d3的內部算法咱們基本干涉不了, 因此d3的力導向圖怎麼動最後停在哪都是咱們無法精確控制的, forceSimulation
定義了力導向圖的基本形態好比key值是否居中等等, 可是到這一步還沒對數據進行任何處理。
const link
和 const node
, 簡單講就是把數據和dom進行綁定插入對應的dom節點, 一直到這一步, 咱們完成了基本的步驟:根據關係模型繪製對應節點, 因爲不是canvas, 每一個數據節點都有一個對應的dom節點, 這裏能夠對樣式進行精確的處理。
截止上面也並非非得用d3不可,就是一些dom插入操做, 原生js也是能夠實現的。 simulation.nodes(nodes).on('tick', ticked)
跟simulation.force('link').links(links)
纔是d3真正的做用所在,它會修改原來的數據模型在上面掛載一些位置信息, 如圖所示:
能夠看到,nodes和link上面分別多了很多數據,暫時咱們不須要了解那麼多, 只要知道x
和y
是節點的位置信息便可,另外力導向圖會不停的tick
(300次左右),每次tick
,d3都會修改graph
上的位置信息,它內部確定作了不少事情, 好比碰撞檢測等等。當每次tick
觸發的時候咱們都已調用一個callback,在這個callback裏更新全部節點的位置信息,也就是上面代碼的ticked
, 咱們就是修改了node和link的位置信息也就是x1
之類的, 這些都是svg提供的接口這裏很少作說明了。 到這裏, 一個完整的力導向圖算是完成了,雖然數據少了點可是並不妨礙咱們去理解其中的原理。
經過上面一個完整示例, 咱們發現,d3的核心並不在於繪製圖形,這些都是dom操做,而是數據的處理,數據驅動dom,到這裏是否是跟現代mvvm又掛上鉤了,而且d3是基於dom的, 咱們徹底能夠把d3當作一個算法庫,處理數據,至於圖像的繪製徹底能夠交由react等框架,這是canvas類庫所作不到的。用上virtual dom性能可能還會更高一點。dom操做是昂貴的,virtual dom跟d3搭配味道可能更佳。若是把d3做爲一個算法庫咱們還缺乏最佳實踐。還須要學習。