這里涉及了 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>