d3js path generator vs layouts

世有因果知因求果發表於2017-07-17

我們知道d3的一般套路就是d3.selectAll('path.mypath').data(yourDataset).enter().append('path').attr('class','mypath').attr('d',thePathString)

而thePathString的獲取,一般都是根據繫結的yourDataset來生成的。具體生成方式有:

1. 通過程式自己一節一節地拼湊起來path的d屬性,比如在d3v4中由於取消了diagonal對角線生成器,我們可以通過下面的程式碼手工拼湊連線節點之間的link:

var link = svg.selectAll(".link")
    .data(root.descendants().slice(1))
  .enter().append("path")
    .attr("class", "link")
    .attr("d", function(d) {
      return "M" + d.y + "," + d.x
          + "C" + (d.y + d.parent.y) / 2 + "," + d.x
          + " " + (d.y + d.parent.y) / 2 + "," + d.parent.x
          + " " + d.parent.y + "," + d.parent.x;
    });

 

2. 通過d3.path()的相關命令API

const context = d3.path();
context.moveTo(50, 50);
context.lineTo(100, 50);
context.arc(100, 100, 50, -0.5 * Math.PI, 0);
context.lineTo(150, 150);
console.log(context.toString())
//輸出 M50,50L100,50A50,50,0,0,1,150,100L150,150

3. 通過d3js built in path generator來建立

d3內建有以下path generator,都在d3-shape模組中可以找到: line, arc, pie,area,radialArea, linkVertical,linkHorizontal(替代d3 v3中的diagonal),linkRadial,symbol

特別是第三種方法在常見的圖表設計中非常常見,也很方便。但是雖然這種方法解放了我們拼湊d屬性的煩惱,但是這些generator的data輸入卻有一定的要求。

比如arc()弧線生成器,它就要求輸入data具有下面的資料結構:

var arcitemdata = {
 startAngle: 0, // 弧線的起始角度
 endAngle: Math.PI *0.6 // 弧線的結束角度
}

area()區域生成器,它就要求輸入點data具有下面的資料結構(或者指定對應的accessor function來獲取到對應的y0,y1資料):

var dataItemForArea =[
 {
 y0: 10, // y0為baseline的y軸值,一般所有的點y0值相等,也可以不等哦
 y1: 20, // y1為topline的y軸值
},
 {
 y0: 10,
 y1: 30,
},
 {
 y0: 10,
 y1: 25
}

而一般來說,我們有的資料只是原始的資料,並不能夠直接用於繪製。(比如沒有startAngle, endAngle我們就無法畫弧線)如何轉換成方便被不同的路徑生成器來使用的資料格式呢?當然你可以自己寫一段程式一一對映,可以想象這,又是一個非常繁瑣的過程。幸運的是d3也為我們考慮到了這些,它提供了被稱為layout的函式,通過這些layout就能將原始的資料,轉換為易於被視覺化繪圖所使用的資料(不一定非要用路徑生成器來做視覺化哦,很多情況下,你可以直接使用d3的select,data,enter,append,attr的模式來直接消費使用這些轉換過來的資料!)

d3為一些複雜的圖表分別內建了不同的layout函式。比如:

pie,Force, Chord, Tree, Cluster, Bundle, Pack, histogram generator(直方圖),partition, Stack, Treemap,ribbon(d3-chord)(和絃圖),geoPath(d3-geo),geoCircle,geoGraticule,axisTop,axisRight,axisBottom,axisLeft等。

比如說,我們給定一個陣列,要求生成餅狀圖,這時我們想到的是首先將原始資料轉換為arc元圖所需資料陣列,隨後應用弧生成器來繪製。根據上面我們提到的,arc弧生成器需要一些startAngle, endAngle以及innerRadius,outerRadius來唯一描述一個弧段。這時,我們就可以應用arc layout來轉換原始資料了!看下面的例子:

var data = [1, 1, 2, 3, 5, 8, 13, 21];
var arcsData = d3.pie()(data);
console.log(arcsData) 
//[
//  {"data":  1, "value":  1, "index": 6, "startAngle": 6.050474740247008, //"endAngle": 6.166830023713296, "padAngle": 0},
//  {"data":  1, "value":  1, "index": 7, "startAngle": 6.166830023713296, //"endAngle": 6.283185307179584, "padAngle": 0},
//  {"data":  2, "value":  2, "index": 5, "startAngle": 5.817764173314431, //"endAngle": 6.050474740247008, "padAngle": 0},
//  {"data":  3, "value":  3, "index": 4, "startAngle": 5.468698322915565, //"endAngle": 5.817764173314431, "padAngle": 0},
//  {"data":  5, "value":  5, "index": 3, "startAngle": 4.886921905584122, //"endAngle": 5.468698322915565, "padAngle": 0},
// {"data":  8, "value":  8, "index": 2, "startAngle": 3.956079637853813, //"endAngle": 4.886921905584122, "padAngle": 0},
//  {"data": 13, "value": 13, "index": 1, "startAngle": 2.443460952792061, //"endAngle": 3.956079637853813, "padAngle": 0},
//  {"data": 21, "value": 21, "index": 0, "startAngle": 0.000000000000000, //"endAngle": 2.443460952792061, "padAngle": 0}
//]
// 接下來我們就可以使用arc generator了
var arcPath = d3.arc()
      arcPath.innerRadius = 0; // 內徑為0,因此就是一個圓了,而不是扇形
      arcPath.outerRadius = 100; // 外徑為100
svg.selectAll('path).data(arcsData).enter().append('path').attr('d',arcPath)

從上面這個pie圖的例子中我們看到經過pie() layout函式變換後,就生成了一堆包含了startAngle, endAngle的物件陣列(角度之和為360度),而這些可以總結出來,在應用layout最終實現資料視覺化時,也有章可循,分為三步:

1.  獲取原始資料

2. 對原始資料呼叫對應的layout來轉換資料(你也可以創作自己的layout函式哦!)

3. 使用路徑生成器或者最原始的視覺化pattern來使用第2.步驟轉換來的中間資料!

相關文章