D3.js 基於數據的繪圖


 

 

 

 

這里涉及了 HTML & CSS 的知識,比如說元素的樣式和元素塊,以后我會把相關知識補上。


 

繪制直線圖

條形圖實際上是矩形,而 HTML 的 div 元素是繪制矩形的最簡單手段。(對於瀏覽器來說,HTML 中的一切元素都可以用來表示矩形)。

所以我們可以定義一個叫 bar 的 div 類,用於存放圖表的公共屬性。(除了高度,其他的屬性應該是共享的)

div.bar {
                display: inline-block;  width: 20px;  height: 75px;    /*最后這里會被覆寫*/  margin-right: 2px;  background-color: green;

 

  •  關於類

元素的類作為 HTML 屬性存在於標記代碼中,同時 CSS 規則也可以引用它。除了為元素設定類以外,直接給元素應用樣式也可以。(這里不太懂,下次遇到案例再寫上)

D3 有一種方法用於快速添加或者刪除元素的類:

.classed("bar",true) // 給選中的元素添加類 bar  .classed("bar",false) //從元素總刪除類 bar 

 

  • 關於樣式

.style 方法用於直接為 HTML 元素應用 CSS 屬性和值。這方法執行的結果等價於在 HTML 的 style 屬性中直接寫入 CSS 規則

<div style="height: 75px;"></div>

如果要生成條形圖,每個條形圖的高度必須是對應數據值得函數,D3 代碼中可以對這個高度之進行重寫:

.style("height", function(d) {
        var barHeight = d * 5; //這里是因為原始生成高度太矮了
        return barHeight + "px"; 
});

 

  • 關於屬性設定

attr() 用於設定HTML 元素的屬性和值。我們要給我們生成的 div 中添加 bar 類,需要這樣寫:

.attr("class","bar")

 

 

代碼如下:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>D3: Our first bar chart with data</title>
        <script type="text/javascript" src="../d3.js"></script>
        <style type="text/css">
            div.bar {
                display: inline-block;
                width: 20px;
                height: 75px;    /* Gets overridden by D3-assigned height below */
                margin-right: 2px;
                background-color: green;
            }
        </style>
    </head>
    <body>
        <script type="text/javascript">
            var dataset = [ 25, 7, 5, 26, 11 ];
            d3.select("body").selectAll("div")
                .data(dataset)
                .enter()
                .append("div")
                .attr("class", "bar")
                .style("height", function(d) {
                    var barHeight = d * 5;
                    return barHeight + "px";
                });

 

隨機數據

教材上面用了一個可以生成隨機數值得方法,在這里記錄一下:

這里是創建了一個名為 dataset 得空數組;

初始化一個循環25次的 for 循環,執行25次

每次生成一個介於0到30之間的隨機數

把新數值追加到數組中(push() 是數組的方法,每次執行都會把一個新值推進數組末尾)。

 

var dataset = [];
for( var i = 0; i < 25 ;i ++){
    var newNumber= Math.random() * 30;
    dataset.push( newNumber);}

注意:

此時 push 進去的都是浮點數,可以用 Math.round() 或者Math.floor () 方法取整。前者是將數值想上取整,后者是向下取整。

var newNumber = Math.floor(Math.random() * 30);
dataset.push( newNumber();)

 


繪制 SVG

SVG 元素是通過標簽中的屬性 / 值對來指定SVG元素的個各方面特征,如:

<element property = "value"></element>

因為 SVG 元素存在於 DOM 中,與其他 HTML 元素一樣,因此生成 SVG 圖形依然要使用 append() 和 attr() 方法。

創建 SGV

首先要創建一個元素,以便在其中保存所有圖形。這行代碼先找到文檔的 body 元素,然后再結束的 </body> 標簽前添加一個新的 svg 元素。

 d3.select("body").append("svg");

也可以使用一種更好的方式,把append() 返回的新元素保存在了變量 svg 中。有了這個引用,將來可以少些很多代碼,從而不用總是寫 da.select("svg") ,而是只要寫 svg 即可:

var svg = d3.select("body").append("svg");

 完整代碼如下,DOM 中將創建一個新的空的 SVG元素。其中高度和寬度保存於變量中,可以方便引用。

var w =500;
var h = 50;
var svg = d3.select("body")     .append("svg")  .attr("width",w)  .attr("height",h);

 然使用data() 迭代每個數據節點,創建一個圓形。同時創建一個新的變量保存引用

var circles = svg.selectAll("circle")  .data(dataset)  .enter  .append("circle");

創建位置和大小信息:

circle.attr( "cx", function(d, i)){
    return (i*50) +25;  
  // 這里是通過引用所有的圓形的變量來設置每一個圓形的屬性。(在SVG中,cx 是圓形圓心的 x 坐標,由於數據已經綁定到了圓形,所以對於每個圓形來說,都有其對應於原始數據的值。
  // 並且其中的i值是自動生成的),同時,索引 i 是從function(d,i)中傳入的,使其與 d 元素一致。
}) .attr("cry", h/2) .attr("r", function(d) { return d; });
  // cy 是圓形圓心的 Y 的坐標,這里把 cy 設置成了 h 的一半。由於 h 保存着整個SVG元素的高度,所以這里是將所有圓形垂直居中
  // 每個圓形的半徑被設為 d,從而反映數據的大小

最后生成如圖示:

也可以在上面添加色彩:

色彩填充( fill )和描邊( stroke )同樣也是屬性,所以也可以通過attr()方法來設定。

.attr("fill", "yellow")
.attr("stroke", "orange")
.attr("stroke-width", function(d) {
return d/2;}

最后圖像如下:

 

 

源代碼為:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>D3: Using color in SVG</title>
        <script type="text/javascript" src="../d3.js"></script>
        <style type="text/css">
            /* No style rules here yet */        
        </style>
    </head>
    <body>
        <script type="text/javascript">
            //Width and height
            var w = 500;
            var h = 100;
            //Data
            var dataset = [ 5, 10, 15, 20, 25 ];
            //Create SVG element
            var svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);
            var circles = svg.selectAll("circle")
                .data(dataset)
                .enter()
                .append("circle");
            circles.attr("cx", function(d, i) {
                        return (i * 50) + 25;
                    })
                   .attr("cy", h/2)
                   .attr("r", function(d) {
                        return d;
                   })
                   .attr("fill", "yellow")
                   .attr("stroke", "orange")
                   .attr("stroke-width", function(d) {
                        return d/2;
                   });
        </script>
    </body>
</html>

 關於繪制條形圖的改進:

 我們也可以通過 SVG 的方式來繪制條形圖:

首先確定 SVG 的大小:

var w = 500;
var h = 500;

然后讓 D3 創建一個空元素,將其添加到 DOM 中.復習一下,這些代碼會在結束的</body>標簽前面插入新的 svg 元素,並將結果保存在了變量 svg 中,因此以后可以方便引用這個 sv g元素,而不用每次再使用 select() 之類的代碼重新選擇。

var svg = d3.select("body")
                    .append("svg")
                    .attr("width",w)
                    .attr("height",h);

然后不創建 div,而是生成矩形元素 rect 並將其添加到 svg 中。

這段代碼選擇了 svg 中所有的矩形。但是,現在什么也沒有,所以會返回一個空的元素集。

 接下來 data(dataset) 看到了數據集中有20個值,就把這些值交給了 enter() 處理。每個rect  必須有 x, y width 和 height 屬性。這里就是用 attr() 為每個新創建的 rect 設置了這些屬性。但是這樣會出現一個問題,就是所有的條形生成以后就重疊在了一起,而且此時並沒有反映數據。 

svg.selectAll("rect")
    .data(dataset)
    .enter()
    .append("rect")
    .attr("x",0)
    .attr("y",0)
    .attr("width",20)
    .attr("height",100);

為了解決重疊問題們同樣需要使用 function() 函數:

.attr("x",function(d,i)) {
    return i*21; //由於矩形寬20像素,所以外加一個像素作為間距。
};

動態縮放:

當數據比較多的時候,最右邊的矩形很有可能跑到 SVG 外面去,這時候就需要使用靈活、動態的坐標方案了。

首先,從改進設置每條矩陣 x 坐標的那行代碼進行修改,這樣的話,每個矩形就會進行縮放生成。當數據密的時候就會密集,當數據稀疏的時候間距就會拉大。

.attr("x",function(d,i) {
    return i * (w / dataset.length);
})

以圖為例:

當數據密集的時候:

當數據稀疏的時候:

這種解決方式並不好看。為了更加的美觀,可以將矩形的寬度也成比例的縮放。

var w = 500;
var h = 100;
var barPadding = 1; //這里的變量是減去的間距的值
.attr("width",w / dataset.length - Padding)

然后再讓數據值決定條形高度:

.attr("height",function(d)){
    return d*4; // 放大四倍
});

結果如圖所示:

由於 SVG 在繪制時,x 和 y 值指定的是它們左上角的坐標,所以 SVG 支持的只有左上角坐標系。如果我們需要改成一般的矩形圖,就需要將每個圖形的“下沿”與 SVG 的下沿對齊,每個 rect 的 height可以就設置為數據值的本身。

.attr("y", function(d){
    return h - d;
})
.attr("height",function(d){
    return d;
});

結果如圖示:

上色

使用 fill 屬性可以對其添加顏色:

.attr(" fill", "teal");

我們也可以讓顏色反映數據的某些特性,對着條形圖而言,這樣做叫做雙重編碼,即同樣的數據值可以被編碼成倆種可以見的特性:條形高度和顏色。

.attr( "fill", function(d){
    return "rgb( 0, 0, "+(d * 10) +" )";

附錄:關於多值映射

D3 擁有多值映射機制,從而可以一次性設置多個值。而且依然是用 attr( ) 方法。假設要把一個圓形平移到 SVG 左上角,再設置成紅色,可以每次單獨調用 attr( ) :

svg.select("circlr")
    .attr("cx",0)
    .attr("cy",0)
    .attr( "fill", "red")

也可以把這三個屬性的值都封裝在一個對象中,然后統一交給 attr( ) :

svg.select("circlr")
    .attr({
        cx: 0;
        cy: 0;
        fill: "red"
    });

 

源代碼為:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>D3: Adding dynamic color, based on data</title>
        <script type="text/javascript" src="../d3.js"></script>
        <style type="text/css">
            /* No style rules here yet */        
        </style>
    </head>
    <body>
        <script type="text/javascript">

            //Width and height
            var w = 500;
            var h = 100;
            var barPadding = 1;
            
            var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
                            11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
            
            //Create SVG element
            var svg = d3.select("body")
                        .append("svg")
                        .attr("width", w)
                        .attr("height", h);

            svg.selectAll("rect")
               .data(dataset)
               .enter()
               .append("rect")
               .attr("x", function(d, i) {
                       return i * (w / dataset.length);
               })
               .attr("y", function(d) {
                       return h - (d * 4);
               })
               .attr("width", w / dataset.length - barPadding)
               .attr("height", function(d) {
                       return d * 4;
               })
               .attr("fill", function(d) {
                    return "rgb(0, 0, " + Math.round(d * 10) + ")";
               });
            
        </script>
    </body>
</html>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM