拖更了很久,最近畢業的手續辦的差很少了,應該能夠回來了...css
系列傳送門:html
知識點:git
數據可視化的第一步仍是數據讀取,在d3中可使用d3.csv
很是方便的讀取數據,它會返回一個Promise
對象。github
csv文件是以逗號,
分隔的數據內容,本地使用的csv數據以下,文件名爲data.csv
:chrome
country,population
China,1415046
India,1354052
United States,326767
Indonesia,266795
Brazil,210868
Pakistan,200814
Nigeria,195875
Bangladesh,166368
Russia,143965
Mexico,130759
複製代碼
首先將數據讀入,代碼以下:數組
const data = d3.csv('data.csv').then(data => {
console.log(data))
})
複製代碼
能夠看到,控制檯輸出了一個數組,數組中的每條數據均是一個對象,類型爲{country:xx, population:xx}
安全
固然,人數天然應該是Number
類型的,同時,爲了將人數轉換爲單位個
,將全部數據都擴大一千倍,即:服務器
const data = d3.csv('data.csv').then(data => {
data.forEach(element => {
element.population = +element.population * 1000
}); //處理完數據,就能夠開始畫圖了
render(data)
})
複製代碼
HTML文件與上一節相同,都是僅包含了一個<svg></svg>
標籤,首先選擇svg:app
const svg = d3.select('svg');
const height = +svg.attr('height');
const width = +svg.attr('width');
const render = data => {
} //根據已有數據,畫圖渲染的函數
複製代碼
將數據綁定到DOM上,是D3最大的特點。d3.select
與d3.selectAll
返回選擇集,但其自己是沒有數據的,經過data()函數,能夠將數據與之綁定。相關函數有兩個:dom
selection.datum([value])
選擇集上的每一個元素都綁定相同的元素valueselection.data(values[,key])
選擇集上每個元素分別綁定數組values的每一項,key是一個鍵函數,用於指定綁定數組時的規則。datum
用比較少,這裏主要用到的是data()
,將已處理好的數據綁定在dom上。
在進行數據綁定的時候,不必定數據和元素個數就是相同的,這個時候就須要一個動態的處理,這就須要用到updata
、enter
、和exit
了。
本項目主要用到了enter
,由於頁面中只有svg
標籤,咱們須要作的是根據數據內容,在svg
中畫<rect>
來表示柱狀圖。 理解 Update、Enter、Exit
有了以上的概念,就能夠開始畫圖了
const render = data => {
svg.selectAll('rect').data(data) //選擇`rect`並綁定數據data,但這個時候沒有元素,所以使用enter
.enter().append('rect')
.attr('width',width)
.attr('height','30px')
}
複製代碼
這樣更新視圖,就能夠看到已經有圖像出來了。可是隻能看到一個黑色的長方形。由於目前圖形並不能反映任何數據,只是單純的固定'width'的長方形。爲此,須要用上數據,可是由於數據可能很大或者很小,爲了讓其可以正好顯示的視圖中,須要使用到比例尺。
D3中有不少比例尺,本例中主要使用到了線性比例尺(scaleLinear)和序數比例尺(scaleBand)
線性比例尺能夠將domain
的內容線性映射到range
的一個範圍內,這樣,就能夠保證不管初始數值多大或多小,都可以很好的適應畫當前視圖。 映射關係:
序數比例尺不是一個連續的比例尺,domain()
中使用一個數組,range()
是一個連續域。 映射關係:
所以,加上兩個方向的比例尺,讓柱狀圖的雛形開始慢慢出現吧:
const render = data => {
const xScale = d3.scaleLinear()
.domain([0,d3.max(data, d => d.population)])
.range([0,width]) //最大值將視圖空間充滿
const yScale = d3.scaleBand()
.domain(data.map(d => d.country))
.range([0,height])
svg.selectAll('rect').data(data) //選擇`rect`並綁定數據data,但這個時候沒有元素,所以使用enter
.enter().append('rect')
.attr('y',d => yScale(d.country))
.attr('width',d => xScale(d.population)) //寬度根據數據
.attr('height',yScale.bandwidth()) //高度由比例尺自動生成
}
複製代碼
這樣子,就有一個雛形了,效果以下:
首先,從新審視下代碼,發現其中d => d.population
以及d => d.country
在比例尺設置以及使用時出現了屢次,若是須要修改,又是代碼中多處的重複修改。爲此,對其進行一個處理,以下:
const render = data => {
const xValue = d => d.population; //優化
const yValue = d => d.country; //優化
const xScale = d3.scaleLinear()
.domain([0,d3.max(data,xValue)]) //優化
.range([0,width])
const yScale = d3.scaleBand()
.domain(data.map(yValue)) //優化
.range([0,height])
svg.selectAll('rect').data(data)
.enter().append('rect')
.attr('y',d => yScale(yValue(d))) //優化
.attr('width',d => xScale(xValue(d))) //優化
.attr('height',yScale.bandwidth())
複製代碼
下圖所示是margin
的佈局示意圖,由於直接按照svg
的height
和width
撐滿畫布將致使沒有多餘的位置放置座標軸等,因此這裏使用一個margin
來對佈局從新規劃。
代碼以下:
const margin = {left:50,top:10,right:20,bottom:30};
const innerHeight = height - margin.top - margin.bottom;
const innerWidth = width - margin.left - margin.right;
複製代碼
其中innerHeight和innerWidth是柱狀圖的實際佔有高度,所以,柱狀圖的代碼能夠修改成:
const xScale = d3.scaleLinear()
.domain([0, d3.max(data,xValue)])
.range([0, innerWidth]) //將 width 改成 innerWidth
const yScale = d3.scaleBand()
.domain(data.map(yValue))
.range([0, innerHeight]) //將height 改成 innerHeight
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`) //加入新元素g,總體移動maring.left和margin.top
g.selectAll('rect').data(data)
.enter().append('rect')
.attr('y',d => yScale(yValue(d)))
.attr('width', d => xScale(xValue(d)))
.attr('height',yScale.bandwidth())
複製代碼
座標軸的繪製,是d3經過<svg>
中的<path>
<text>
<line>
實現的,用到的函數如axisLeft
axisBottom
等,繪製通常分爲一下幾個步驟:
var axisX = d3.axisLeft(xScale)
根據比例尺建立座標軸<g>
組 var gAxis = svg.append('g')
axisX(gAxis)
或者 在上一步直接svg.append('g').call(axisX)
所以,本例中座標軸添加能夠這樣:
g.append('g').call(d3.axisLeft(yScale)); //左邊顯示country
g.append('g').call(d3.axisBottom(xScale))
.attr('transform',`translate(0,${innerHeight})`) //雖然是bottom,可是默認位置並不在下,須要移動至下方
複製代碼
如今柱狀圖仍是很醜的狀態,應該增長一點間隙,讓它看起來更加美觀,這就很是簡單了,在yScale
上使用padding
屬性。
const yScale = d3.scaleBand()
.domain(data.map(yValue))
.range([0, innerHeight])
.padding(0.15) //增長了這個屬性
複製代碼
爲了讓柱狀圖看起來更美觀,增長一些css樣式,樣式以下:
body html{
margin:0;
overflow: hidden;
}
rect {
fill:steelblue;
}
text {
font-size: 1.1em;
}
複製代碼
因爲chrome的安全緣由限制,在本地使用d3.csv
讀取本地文件時是會遇到問題的,並不支持file//:
讀取內容。 所以,代碼在github中是能夠正常運轉,可是本地可能沒法正常運做, 能夠開一個本地服務器,將代碼放置上。
完整代碼詳見:d3系列教程源碼