天天看點

D3.js-柱形圖

柱形圖,是使用柱形的長短來表示資料變化的圖表,也是最簡單的圖表之一。一般情況下,柱形圖包括:矩形、坐标軸和文字。

一、矩形和文字

定義一個數組,每個資料項表示矩形的長短:

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; // 矩形所占寬度(不包括空格)           

複制

D3.js-柱形圖

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的完整示例

D3.js-柱形圖
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;
            });
}           

複制