抢注百度搜索高排名流量域名、品牌商标保护、微信绿标安全域名、备案精品短域名,上铭正知产!

 找回密碼
 加入我們

D3js之入門

[複製鏈接]
老黑醬 發表於 2021-2-7 21:05:22 | 顯示全部樓層 |閱讀模式

相對於echart, highchart等其他圖表庫算是一個比較底層的可視化工具,簡單來講他不提供任何一種現成的圖表,所有的圖表都是我們在它的庫裡挑選合適的方法構建而成。

基於上面的理解,d3無疑會複雜很多但是也強大自由的多,另外因為d3基於svg所以修改圖表的樣式和結構也會方便很多,但是同樣是這個原因,d3的性能比canvas類庫差了不少,dom畢竟是拖累瀏覽器性能的罪魁禍首。順口提一句,d3也是可以基於canvas構建圖表的。但是這篇文章就不提了。

基本概念

對於d3我們可以簡單的將其分個類:數據處理, dom處理,事件以及其他。 其實dom和事件其實可以合到一起。

前端做可視化的時候肯定需要對數據進行處理,d3提供了一下常用的方法。

因為d3是基於svg所以跟dom打交道肯定是必須的,這裡一定程度替代了jQuery之類的功能。

事件的話其實就是一些交互比如滾輪,拖拽等等都是基礎功能可以進行一系列組合排序

請求就是ajax請求數據源了。

數據處理

數據處理就很簡單了,就是對於數組和集合以及時間的一些處理方法, 比如數組求中位數方差等等,和lodash的一些方法有重合,但是還是偏向數學方面,方法有點多這裡不一一列出了:

/
  1. / array的方法
  2. d3.min([1, 2, 3, 4]) // 1 不同於Math,min忽略NaN undefined等
  3. d3.range(1, 10) // [1, 2 ... 10]

  4. // collection的方法
  5. d3.entries({foo: 42, bar: true}); // [{key: "foo", value: 42}, {key: "bar", value: true}]
  6. var map = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
  7. map.get("foo"); // {"name": "foo"}
  8. map.get("bar"); // {"name": "bar"}
  9. map.get("baz"); // undefined

  10. // 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處理

關於dom操作d3也提供了一系列方便的接口,比如d3.select,d3.append等等, 這部分的接口相當多,個人也沒法一一說明, 只能說用法都是一樣的,和jQuery相當類似:

  1. svg.selectAll("circle")
  2.   .data(data)
  3.   .enter().append("circle")
  4.   .attr("cx", function(d) { return d.x; })
  5.   .attr("cy", function(d) { return d.y; })
  6.   .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 中。
image

如上都是鏈式操作

事件

不同於canvas這裡可以直接觸發原生事件,讓人親切很多。

事件是指基於dom的一些交互操作,包括但不限於click等原生事件,類似jQuery,事件是通過on進行綁定的:

selection.on('click', function (d) {}) // this指向事件元素, d是綁定的數據可以直接使用
同時,d3提供了很多自定義事件諸如drag, zoom,brush等等,這時候就是通過call調用了:

  1. const brush = d3.brushX()
  2.   .extent([[50, 50], [1100, 150]])
  3.   .on('start brush', brushed)
  4.   .on('end', brushended)

  5. svg.append("g")
  6.   .call(brush)
複製代碼

上面是調用brush事件,同時調用相應的回調, 都是字面意思,至於還有很多有意思的事件,都隱藏在文檔中。

其他

這個其他就包含了很多東西, 比如異步請求,解析excel,動畫等等,這裡不一一說明了, 但是如果發現有需求沒法實現不妨看看文檔,說不定就內置了呢。

完整示例

下面給個示例,簡單力導向圖示例jsfiddle:

image

核心代碼如下:

  1. const height = 200
  2. const width = 200

  3. const svg = d3.select('body').append('svg')

  4. const graph = {
  5.     nodes: [
  6.       { id: 1, name: 'test1' },
  7.     { id: 2, name: 'test2' }
  8.   ],
  9.   links: [
  10.       { source: 1, target: 2 }
  11.   ]
  12. }

  13. const simulation = d3.forceSimulation()
  14.   .force('charge', d3.forceManyBody().strength(-700).distanceMin(100).distanceMax(1000))
  15.   .force('link', d3.forceLink().id(d => d.id))
  16.   .force('center', d3.forceCenter(width / 2, height / 2))
  17.   
  18. const link = svg.selectAll('link')
  19.   .data(graph.links)
  20.   .enter()
  21.   .append('line')
  22.   .attr('class', 'link')  

  23. const node = svg.selectAll('node')
  24.   .data(graph.nodes)
  25.   .enter().append('g')
  26.   .attr('class', 'node')
  27.   
  28. node.append('circle')
  29.     .attr('r', 13)
  30.     .attr('fill', '#999')

  31. node.append('text')
  32.   .attr('dx', -18)
  33.   .attr('dy', 8)
  34.   .style('font-family', 'overwatch')
  35.   .style('font-size', '18px')
  36.   .text(d => d.name)


  37. const ticked = function () {
  38.   link.attr('x1', d => d.source.x)
  39.     .attr('y1', d => d.source.y)
  40.     .attr('x2', d => d.target.x)
  41.     .attr('y2',  d => d.target.y);
  42.    
  43.   node.attr('transform', d => `translate(${d.x}, ${d.y})`)
  44. }


  45. const { nodes, links } = graph

  46. simulation.nodes(nodes).on('tick', ticked)
  47. 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真正的作用所在,它會修改原來的數據模型在上面掛載一些位置信息, 如圖所示:

image

可以看到,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作為一個算法庫我們還缺少最佳實踐。還需要學習。
回復

使用道具 舉報

您需要登錄後才可以回帖 登錄 | 加入我們

本版積分規則

备案权重域名预定

QQ|4um創業社區

GMT+8, 2024-5-20 13:13

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回復 返回頂部 返回列表