d3.js直方圖與座標軸基礎

d3.js直方圖與座標軸基礎

LSD_Monkey

這裏講一下怎麼樣用d3.js,輸入一個數據list,根據數據畫一個帶有座標軸的簡單直方圖.
以下是目標效果.

 

a plot

直方圖部分


引入d3.
<script src="//d3js.org/d3.v4.min.js"></script>

畫矢量圖需要畫布,首先,創建新svg:

<script>
var width = 800;  //定義寬度
var height = 800;   //定義高度
var svg = d3.select("body")     //選擇<body>標籤
    .append("svg")          //在其中加入<svg>標籤
    .attr("width", width)      //設定寬度 
    .attr("height", height)   //設定高度
    .attr("style", "border: 1px solid black"); //給一個邊框
</script>

空白 svg

我的數據爲一個數組:var dataset = [ 25, 6, 21 , 17 , 13 , 9, 6, 2, 20 ];
我想讓我的直方圖向右,縱向排列.
每一個矩形的x向長度既表示了數據的大小.
那麼第一個要解決的問題就是,怎麼把原始數據換算成像素,肯定不能直接畫25px,6px...
這個時候就需要一個比例尺了.

d3提供的連續比例尺可以這麼寫:

var linear = d3.scaleLinear()
        .domain([0, d3.max(dataset)])
        .range([ 0,400]);

定義變量linear爲一個連續比例尺,定義域.domain,值域.range.
在這裏,定義域 .domain([0, d3.max(dataset)])是指,我比例尺輸入的數據最小爲0,最大是原dataset裏最大的那個數(25),所以輸入比例尺的定義域爲0到25,換算後的值域爲0到400px,既——數據0對應0px,數據25對應400px.

有了長度比例尺,就可以畫直方圖了.

var rectHeight = 30;   
svg.selectAll("rect")
    .data(dataset)
    .enter()
    .append("rect")
    .attr("x",0)
    .attr("y",function(d,i){
         return i * (rectHeight + 5);
    })
    .attr("width",function(d){
         return linear(d);
    })
    .attr("height",rectHeight)
    .attr("fill","steelblue")
    .attr("opacity",0.4);

在這裏,先定義了每一個矩形框框的高度爲30px,
.data()綁定數據dataset
.enter()方法是專門用在這種,根據輸入數據動態新建元素的情況下的,具體可以參考官方說明:

# selection.enter() <>
Returns the enter selection: placeholder nodes for each datum that had no corresponding DOM element in the selection. (The enter selection is empty for selections not returned by selection.data.)
**The enter selection is typically used to create “missing” elements corresponding to new data. **For example, to create DIV elements from an array of numbers:

var div = d3.select("body") .selectAll("div")
  .data([4, 8, 15, 16, 23, 42]) 
  .enter()
  .append("div")
  .text(function(d) { return d; });

If the body is initially empty, the above code will create six new DIV elements, append them to the body in-order, and assign their text content as the associated (string-coerced) number:

<div>4</div>
<div>8</div>
<div>15</div>
<div>16</div>
<div>23</div>
<div>42</div>

Conceptually, the enter selection’s placeholders are pointers to the parent element (in this example, the document body). The enter selection is typically only used transiently to append elements, and is often merged with the update selection after appending, such that modifications can be applied to both entering and updating elements.

所以,在這裏,我們既是根據輸入dataset來新建<rect>,
怎麼根據呢?
屬性設置裏面:

    .attr("x",0)
    .attr("y",function(d,i){
         return i * (rectHeight + 5);
    })
    .attr("width",function(d){
         return linear(d);
    })
    .attr("height",rectHeight)

表示的是,對於每一個新建的矩形<rect>,x座標爲0,y座標爲序號*矩形高度+5px. (5px是矩形之間的空格)
可以理解爲,在(0,0)放置一個高度爲30px的矩形,再在(0,35)放一個,再在(0,70)放一個,再在(0,105)放一個...
而矩形的寬度要和我們的數據保持一致,所以width屬性,輸入了一個無名函數

function(d){
    return linear(d);
}

這個函數是什麼意思呢,當選擇集需要使用被綁定的數據時,常需要這麼使用。其包含兩個參數,其中:

  • d 代表數據,也就是與某元素綁定的數據
  • i 代表索引,代表數據的索引號,從 0 開始
    所以在這裏,每個矩形的寬度,是通過比例尺返回的像素值return linear(d).
    當完成這一部後,我們的矩形就顯示出來了. 其中最長的一根應該就是代表25的400像素,看右邊監視欄的第一個rect,正好就是400的寬度.
    <rect x="0" y="0" width="400" height="25" fill="steelblue" opacity="0.4"></rect>

顯示出的矩形

座標軸部分


有了直方圖的矩形,我們現在來畫一個座標軸.
座標軸有4種:

座標軸類型

 

分別是什麼樣子的呢,我們來試一試.
在API使用規範裏這樣寫到:

Regardless of orientation, axes are always rendered at the origin. To change the position of the axis with respect to the chart, specify a transform attribute on the containing element. For example:

d3.select("body").append("svg") 
        .attr("width", 1440) 
        .attr("height", 30)
    .append("g") 
        .attr("transform", "translate(0,30)") 
        .call(axis);

意思是,在使用座標軸的時候,必須先添加一個<g>元素,再.call(axis)調用axis. 而默認座標軸都是從原點(0,0)開始畫,如果需要調整位置,在屬性transform裏設置translate(x,y)

所以,我們測試一下在(30,30)畫一個axisTop.

var axis = d3.axisTop(linear);//新建一個axisTop座標軸,比例尺爲linear
  svg.append("g") //調用
        .attr("transform", "translate(30,30)")//位置(30,30)
        .call(axis);

我們得到結果如下:

 

座標軸

 

而每個座標軸是由哪些部分組成的呢:

 

座標軸的組成


一個橫線軸<path class="domain>,和許許多多的<g class="tick">
而且在每一個<g class="tick">內部,有一個<line>和一個<text>.
<line>就是用來畫座標軸每一個小分割那條小豎線的,text就是座標軸上的數據.
那麼,我們要怎麼才能修改這些呢.
查閱API得知axis.tickValues([values])可以直接用來傳入一系列座標軸分割點.
試一試:

 

var axis = d3.axisTop(linear)
          .tickValues([0,1, 2, 3, 5, 8, 13, 21,25]);

 

自定義左座標軸分割


果然,座標軸根據我們輸入的數據進行了分割.
我們還可以用axis.tickSize([size])來更改每一個分割的大小.
這一次我們畫一個向下的座標軸,設定每一個分割爲300px:

 

var axis = d3.axisBottom(linear)
          .tickSize(300)
          .tickValues([0,1, 2, 3, 5, 8, 13, 21,25]);

svg.append("g") //調用
        .attr("transform", "translate(0,0)")//位置(0,0)
        .call(axis);

改變分割長度

除此之外,我們還可以改樣式.
我們知道了每一個座標軸有一個<path class="domain>,我們可以試試更改樣式.

 svg.selectAll("path")
        .attr("stroke","red").attr("stroke-width","5" );

更改path樣式

而且在每一個<g class="tick">內部,有一個<line>和一個<text>.
<line>,這兩個元素的可用屬性可以參考linetext
最終,實現效果:

var axis = d3.axisBottom(linear)
          .tickSize(300)
          .tickValues([0,1, 2, 3, 5, 8, 13, 21,25]);

svg.append("g")
        .attr("transform", "translate(10,10)")
   .call(axis);

svg.selectAll("line")
        .attr("stroke","green")
        .attr("stroke-dasharray","2.2");

svg.selectAll("text")
        .attr("dy","2em");

svg.select(".domain").remove()

a plot

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