柱形圖,是使用柱形的長短來表示資料變化的圖表,也是最簡單的圖表之一。一般情況下,柱形圖包括:矩形、坐标軸和文字。
一、矩形和文字
定義一個數組,每個資料項表示矩形的長短:
var dataset = [50, 43, 120, 87, 99, 167, 142]; // 資料集
複制
定義一個SVG,表示繪制區域:
var width = 400; // svg可視區域寬度
var height = 400; // svg可視區域高度
var svg = d3.select("body")
.append("svg") // 在body中添加SVG
.attr("width", width)
.attr("height", height);
複制
添加SVG後,定義幾個變量:
var padding = {top: 20, right: 20, bottom: 20, left: 20}; // 邊距
var rectStep = 35; // 矩形所占寬度(包括空格)
var rectWidth = 30; // 矩形所占寬度(不包括空格)
複制

data()的工作過程:
data()能将數組各項分别綁定到選擇集的各元素上,并且能指定綁定的規則。當數組長度與元素數量不一緻時,data()也能夠處理。當數組長度大于元素數量時,為多餘資料預留元素位置,以便将來插入新元素;當數組長度小于元素數量時,能夠擷取多餘元素的位置,以便将來删除。
在D3中,根據數組長度和元素數量的關系,分别把各種情況歸納如下:
- update:數組長度 = 元素數量;
- enter:數組長度 > 元素數量;
- exit:數組長度 < 元素數量。
繪制矩形:
/* rect */
var rect = svg.selectAll("rect")
.data(dataset) // 綁定資料
.enter() // 擷取enter部分
.append("rect") // 添加rect元素,使其與綁定數組的長度一緻
.attr("fill", "steelblue")
.attr("x", function(d, i){
return padding.left + i * rectStep;
})
.attr("y", function(d){
return height - padding.bottom - d;
})
.attr("width", rectWidth)
.attr("height", function(d){
return d;
});
複制
繪制文字:
/* text */
var text = svg.selectAll("text")
.data(dataset) // 綁定資料
.enter() // 擷取enter部分
.append("text") // 添加text元素,使其與綁定數組的長度一緻
.attr("fill", "white")
.attr("font-size", "14px").attr("text-anchor", "middle")
.attr("x", function(d, i){
return padding.left + i * rectStep;
})
.attr("y", function(d){
return height - padding.bottom - d;
})
.attr("dx", rectWidth/2).attr("dy", "1em")
.text(function(d){
return d;
});
複制
二、更新資料
更新資料後,柱形圖也得跟着變化。
update部分的處理方法是更新屬性,enter部分的處理方法是添加元素後再賦予其相應的屬性,exit部分的處理方法則是删除掉多餘的元素。
矩形:
var updateRect = svg.selectAll("rect").data(dataset); // 錯誤 d3.selectAll() 出了svg範圍
var enterRect = updateRect.enter();
var exitRect = updateRect.exit();
// update處理方法
updateRect.attr("fill", "steelblue")
.attr("x", function(d, i){
return padding.left + i * rectStep;
})
.attr("y", function(d){
return height - padding.bottom - d;
})
.attr("width", rectWidth)
.attr("height", function(d){
return d;
});
// enter處理方法
enterRect.append("rect").attr("fill", "steelblue")
.attr("x", function(d, i){
return padding.left + i * rectStep;
})
.attr("y", function(d){
return height - padding.bottom - d;
})
.attr("width", rectWidth)
.attr("height", function(d){
return d;
});
// exit處理方法
exitRect.remove();
複制
文字:
var updateText = svg.selectAll("text").data(dataset);
var enterText = updateText.enter();
var exitText = updateText.exit();
// update處理方法
updateText.attr("fill", "white")
.attr("font-size", "14px").attr("text-anchor", "middle")
.attr("x", function(d, i){
return padding.left + i * rectStep;
})
.attr("y", function(d){
return height - padding.bottom - d;
})
.attr("dx", rectWidth/2).attr("dy", "1em")
.text(function(d){
return d;
});
// enter處理方法
enterText.append("text").attr("fill", "white")
.attr("font-size", "14px").attr("text-anchor", "middle")
.attr("x", function(d, i){
return padding.left + i * rectStep;
})
.attr("y", function(d){
return height - padding.bottom - d;
})
.attr("dx", rectWidth/2).attr("dy", "1em")
.text(function(d){
return d;
});
// exit處理方法
exitText.remove();
複制
三、坐标軸
比例尺分為:定量比例尺(定義域是連續的)和序數比例尺(定義域是不連續的)。
// 定義柱形圖比例尺
var xAxisWidth = 300; // x軸寬度
var yAxisWidth = 300; // y軸寬度
/* x軸比例尺(序數比例尺) */
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, xAxisWidth], 0.2);
/* y軸比例尺(線性比例尺) */
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([0, yAxisWidth]);
複制
有了比例尺後,矩形的位置、長度都要用比例尺來計算。這麼做之後,資料和繪制就能完全分開,而且隻需要修改比例尺,即可将圖表自由伸縮。
selection.attr("fill", "steelblue")
.attr("x", function(d, i){
return padding.left + xScale(i);
})
.attr("y", function(d){
return height - padding.bottom - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d){
return yScale(d);
});
複制
/* 添加坐标軸 */
var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
yScale.range([yAxisWidth, 0]); // 重新設定y軸比例尺的值域,與原來的相反
var yAxis = d3.svg.axis().scale(yScale).orient("left");
svg.append("g").attr("class", "axis")
.attr("transform", "translate("+ padding.left +","+ (height - padding.bottom) +")")
.call(xAxis);
svg.append("g").attr("class", "axis")
.attr("transform", "translate("+ padding.left +","+ (height - padding.bottom - yAxisWidth) +")")
.call(yAxis);
複制
四、使用call減少備援
call允許将選擇集自身作為參數,傳遞給某一函數。
d3.selectAll("div").call(myfun);
複制
等價于
function myfun(selection){
// 這裡進行相關操作
selection.attr("name", "value");
}
myfun(d3.selectAll("div"));
複制
使用call的完整示例
var dataset = [50, 43, 120, 87, 99, 167, 142]; // 資料集
var width = 400; // svg可視區域寬度
var height = 400; // svg可視區域高度
var svg = d3.select("body")
.append("svg")
.attr("width", width).attr("height", height);
var padding = {top: 20, right: 20, bottom: 20, left: 50}; // 邊距
var xAxisWidth = 300; // x軸寬度
var yAxisWidth = 300; // y軸寬度
/* x軸比例尺(序數比例尺) */
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, xAxisWidth], 0.2);
/* y軸比例尺(線性比例尺) */
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([0, yAxisWidth]);
/* rect */
var rect = svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.call(rectFun);
/* text */
var text = svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.call(textFun);
/* 添加坐标軸 */
var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
yScale.range([yAxisWidth, 0]); // 重新設定y軸比例尺的值域,與原來的相反
var yAxis = d3.svg.axis().scale(yScale).orient("left");
svg.append("g").attr("class", "axis")
.attr("transform", "translate("+ padding.left +","+ (height - padding.bottom) +")")
.call(xAxis);
svg.append("g").attr("class", "axis")
.attr("transform", "translate("+ padding.left +","+ (height - padding.bottom - yAxisWidth) +")")
.call(yAxis);
/* rect處理函數 */
function rectFun(selection) {
selection.attr("fill", "steelblue")
.attr("x", function(d, i){
return padding.left + xScale(i);
})
.attr("y", function(d){
return height - padding.bottom - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d){
return yScale(d);
});
}
/* text處理函數 */
function textFun(selection){
selection.attr("fill", "white")
.attr("font-size", "14px").attr("text-anchor", "middle")
.attr("x", function(d, i){
return padding.left + xScale(i);
})
.attr("y", function(d){
return height - padding.bottom - yScale(d);
})
.attr("dx", xScale.rangeBand()/2).attr("dy", "1em")
.text(function(d){
return d;
});
}
複制