(原文地址:https://bost.ocks.org/mike/bar/善涨,原文作者:Mike Bostock)
簡介:出于個人學習目的,計劃將 D3 的學習資料逐步翻譯為系列文章亏镰,鑒于本人水平有限,如有不到或錯誤之處拯爽,歡迎大家指正索抓,謝謝!
概述
如果你有一小撥數(shù)據(jù)毯炮,一個數(shù)組:
var data = [4, 8, 15, 16, 23, 42];
簡而來說逼肯,一個柱狀圖就是比例上來正確呈現(xiàn)該數(shù)據(jù)的恰當圖表。本篇教程將介紹如何使用 D3 Javascript 函數(shù)庫來創(chuàng)建柱狀圖桃煎。首先篮幢,我們將介紹 HTML 版的原始數(shù)據(jù)法,然后是更復雜的 SVG 方法备禀,最后提供一個帶動態(tài)過渡的版本洲拇。
本篇教程假設您具備網(wǎng)頁開發(fā)的基礎知識:了解如何編輯頁面并用瀏覽器查看、如何加載 D3 程序庫等曲尸。你也會發(fā)現(xiàn)簡單的從 CodePen 模板修改是最簡單的開始方式赋续。
選擇一個元素
在一般的 JavaScript 編程中,通常一次只能處理一個元素另患,比如要創(chuàng)建一個 div
元素纽乱、設置它的內(nèi)容,然后將其添加到body
:
var div = document.createElement("div");
div.innerHTML = "Hello, world!";
document.body.appendChild(div);
有了 D3 (當然 jQuery 等類似函數(shù)庫也一樣)昆箕,您可以通過選擇器來實現(xiàn)同樣的功能鸦列,通過大批的處理選擇結(jié)果并賦予它們豐富的操作功能,您可以任意處理器中想要的內(nèi)容而不用重復數(shù)寫代碼鹏倘。雖然這看起來像一個小小的挑戰(zhàn)薯嗤,消滅循環(huán)和其他程序流控制可以讓您的代碼更加整潔。
選擇器可以通過很多種方式實現(xiàn)纤泵,最常見的是通過請求一個通過特殊符號構(gòu)建的選擇符
字符串(比如 div
或 .foo
等)骆姐,比如創(chuàng)建一個單一元素的選擇符:
var body = d3.select("body");
var div = body.append("div");
div.html("Hello, world!");
您也可以同樣的處理多種元素:
var section = d3.selectAll("section");
var div = section.append("div");
div.html("Hello, world!");
修改操作的串接
另一種方便的選擇法是通過對選擇器的串接:選擇函數(shù),比如 selection.attr
返回當前的選擇。這樣玻褪,您可以繼續(xù)對該元素進行操作肉渴,比如設置 body
的背景色和文字顏色:
var body = d3.select("body");
body.style("color", "black");
body.style("background-color", "white");
“body
,body
带射,body
同规!”反復的呼叫 body
法可以通過如下的操作串接消除:
d3.select("body")
.style("color", "black")
.style("background-color", "white");
注意,我們再也沒有使用一個 var
類型聲明窟社,在進行畢要的操作之后券勺,選擇結(jié)果可以被丟棄。操作串接讓您可以縮短代碼桥爽,減少過多用于變量命名的時間消耗朱灿。
在學到方法串接的好處之后我們要留意昧识,大多數(shù)操作都返回同樣的選擇內(nèi)容钠四,然而有些方法卻不這樣!比如跪楞,selection.append
返回僅包含新元素的選擇缀去,從而讓您可以對新元素進行串接操作:
d3.selectAll("section")
.attr("class", "special")
.append("div")
.html("Hello, world!"); // 注意:本教程中刻意使用縮進來呈現(xiàn)結(jié)構(gòu)關系。
方法串接只能讓選擇內(nèi)容不斷沉入文檔結(jié)構(gòu)內(nèi)甸祭,此時使用 var
可以保留中間過程從而返回原狀態(tài):
var section = d3.selectAll("section");
section.append("div")
.html("First!");
section.append("div")
.html("Second.");
手動創(chuàng)建圖表
現(xiàn)在缕碎,假設您需要拋棄 JavaScript 而寫一個柱狀圖,反正在數(shù)據(jù)表中只有六個數(shù)據(jù)池户,所以手工寫幾個 div
并不是復雜的事情咏雌,根據(jù)數(shù)據(jù)設置好它們的寬度,不就完成了:
<!DOCTYPE html>
<html>
<head>
<style>
.chart div {
font: 10px sans-serif;
background-color: steelblue;
text-align: right;
padding: 3px;
margin: 1px;
color: white;
}
</style>
</head>
<body>
<div class="chart">
<div style="width: 40px;">4</div>
<div style="width: 80px;">8</div>
<div style="width: 150px;">15</div>
<div style="width: 160px;">16</div>
<div style="width: 230px;">23</div>
<div style="width: 420px;">42</div>
</div>
</body>
</html>
結(jié)果顯示如下:
本圖用一個 div
做容器校焦,每個柱子采用一個子節(jié)點 div
赊抖,子 div
擁有藍色背景色與白色前景色,呈現(xiàn)出了向右對齊的文字標簽寨典。當然您可以移掉容器 div
而讓實現(xiàn)更簡潔氛雪,但一般來說,您的頁面在圖標之外還有其他內(nèi)容耸成,表格容器可以讓您不影響其他頁面的情況下布局和樣式化圖表报亩。
自動創(chuàng)建圖表
手工繪制并不適合現(xiàn)實中絕大多數(shù)數(shù)據(jù)源,本文的目的正是教您如何自動通過數(shù)據(jù)創(chuàng)建圖表井氢,所以讓我們從一個僅包含一個類別為 chart 的 div
的空頁面開始弦追,用 D3 創(chuàng)建同樣的圖表:
d3.select(".chart")
.selectAll("div")
.data(data)
.enter().append("div")
.style("width", function (d) { return d * 10 + "px"; })
.text(function(d) {return d;});
雖然部分代碼看起來熟悉,我們引入了一個新概念:數(shù)據(jù)接合花竞。下面讓我們將它拆開劲件,將以上簡潔代碼寫成原本的加長格式,從而展現(xiàn)它的工作原理。
首先寇仓,我們使用類別選擇器得到圖表容器:
var chart = d3.select(".chart");
下一步举户,我們通過定義需要接合的部位來接合數(shù)據(jù):
var bar = chart.selectAll("div");
數(shù)據(jù)接合,便是在數(shù)據(jù)改變時遍烦,使用標準模式來創(chuàng)建俭嘁、更新和銷毀元素。這感覺上有些奇怪服猪,好處是您可以只學此單一的模式來改變頁面供填,所以無論您構(gòu)建靜態(tài)圖表還是動態(tài)漸變圖表時都可以保持代碼一致,就像您選擇希望存在的元素時的初始選擇器罢猪。(見“使用接合思考”)近她。
下一步,我們使用 selection.data
將數(shù)據(jù)接合到選擇結(jié)果上:
var barUpdate = bar.data(data);
因為我們知道選擇結(jié)果為空膳帕,其返回的 update
和 exit
選擇結(jié)果同樣為空粘捎,我們只需要處理包含了不存在的新數(shù)據(jù)的 enter
選擇結(jié)果。
var barEnter = bar.Update.enter().append("div");
下面我們根據(jù)關聯(lián)的數(shù)據(jù)為每個新柱子設置寬度 d
:
barEnter.style("width", function (d) {
return d * 10 + "px";
});
因為這些元素是通過數(shù)據(jù)接合創(chuàng)建的危彩,每個柱子已經(jīng)綁定了數(shù)據(jù)攒磨,我們通過傳遞一個計算寬度風格的函數(shù)來恰當設置柱子的尺寸。
最后汤徽,我們使用一個函數(shù)來設置每個柱子的文字標簽:
barEnter.text(function (d) {
return d;
});
D3 的選擇器操作符娩缰,比如 attr
,style
谒府,property
拼坎,允許您將數(shù)值設置為常量(每個選擇元素相同)和函數(shù)(每個單獨計算)兩種方式。如果某個屬性必須基于元素綁定的數(shù)據(jù)計算完疫,那么就使用函數(shù)來實現(xiàn)泰鸡,如果每個元素都相同,那么就使用字符串或數(shù)值來賦值趋惨。
用縮放匹配畫幅
以上代碼的軟肋鸟顺,是我們使用固定數(shù)字 10
來轉(zhuǎn)換每個柱子和數(shù)據(jù)的關系。該數(shù)字應當取決于數(shù)據(jù)集內(nèi)容的分布區(qū)域(0到42)和圖表所定制的寬度(420)器虾,當然讯嫂,針對以上圖表,我們選擇 10
是沒有問題的兆沙。
我們可以使用 線性縮放 功能讓以上數(shù)量關系更加明確欧芽,并消滅魔術(shù)數(shù)字 10
,D3 的縮放功能輸入為數(shù)據(jù)范圍及顯示尺寸:
var x = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, 420]);
雖然葛圃,在這里 x
看起來像個對象千扔,它也是一個包含根據(jù)輸入數(shù)值轉(zhuǎn)換原始數(shù)據(jù)的函數(shù)憎妙。比如,傳入 4 可以返回 40曲楚, 輸入 16 返回 160厘唾。為了使用新的縮放,我們將手寫的乘法轉(zhuǎn)換代碼變?yōu)楹艚性摵瘮?shù):
d3.select(".chart")
.selectAll("div")
.data(data)
.enter().append("div")
.style("width", function (d) {
return x(d) + "px";
})
.text(function (d) {
return d;
});
下一篇:創(chuàng)建柱狀圖龙誊,第二部分
本篇原始柱狀圖的呈現(xiàn)很容易實現(xiàn)抚垃,但很顯然也是有限的。柱狀圖應當有格線來輔助數(shù)據(jù)對比趟大,您也可能更喜歡垂直的柱狀圖鹤树,或者您想要其他不同的圖表類型,比如餅狀圖和流狀圖等逊朽。為了更好的視覺表達罕伯,您需要下一篇我們將呈現(xiàn)的 SVG 技術(shù)。
個人筆記
本篇教程之中叽讳,D3 的原創(chuàng)作者通過“非圖表”技術(shù)展示了 D3 的選擇器與修改器等功能追他,然而,選擇器與修改器僅僅是 D3 框架之中最基礎部分的功能绽榛,作者書寫本教程的目的應該是為讀者打下開發(fā)的基礎湿酸,而實際并未深入 D3 作為圖形繪制系統(tǒng)的實質(zhì)核心內(nèi)容婿屹。
使用 div
繪制圖表灭美,可能是十年前 Web 技術(shù)沒有發(fā)展到 HTML5 時,使用網(wǎng)站進行數(shù)據(jù)呈現(xiàn)的“權(quán)宜之計”昂利。然而届腐,當今 HTML5 技術(shù)將 Web 技術(shù)提高到了一定的高度,通過 svg
和 canvas
標簽蜂奸,如今的瀏覽器已經(jīng)成為了強大的矢量繪圖工具(當然犁苏,還有不可忽略的 webgl
圖形接口)。
D3 在 v3 版本及之前扩所,還是一個一體化開發(fā)的普通插件围详,在 v4 到來之時,D3 進行了技術(shù)上的革新祖屏,將原本一體的 Javascript 代碼拆分成了幾十個可以獨立使用的模塊助赞,并已經(jīng)完整提供了對 svg
繪圖組件的全面支持。在 v5 到來之后袁勺,D3 完整支持了 canvas
標簽雹食,并且提供了兩者可以隨意選擇的技術(shù)方案。
我們作為 D3 框架的學習者期丰,特別是掌握了 jQuery 插件使用方法的讀者們肯定感覺到了內(nèi)容的枯燥群叶。但是吃挑,作為未來的 D3 應用者,我們會在未來的開發(fā)使用中越來越發(fā)現(xiàn)這些基礎知識的重要性街立,因為每一幅通過 D3 繪制的 Web 圖表都必須使用以上所描述的內(nèi)容舶衬。
在下一篇,作者將深入 D3 的 svg 支持功能之中赎离,帶領大家進入真正的 D3 領域约炎,來體會高效的繪圖技術(shù)和接口。