D3.js

D3.js

為什么學(xué)習(xí)D3

D3.js和threejs的應(yīng)用場景完全不一樣。threejs主要應(yīng)用與基于webGL的3D場景践剂,而D3.js確主要應(yīng)用與2D場景。

它們一起形成了一種互補(bǔ)關(guān)系遣鼓。

簡而言之D3JS就是一個(gè)數(shù)據(jù)可視化的庫舶衬。

那什么是數(shù)據(jù)可視化呢?

給出一組數(shù)據(jù) [10长赞,80晦攒,40,100,30,20,50]

image-20190801162422591.png

類似的庫 eharts

ECharts,一個(gè)使用 JavaScript 實(shí)現(xiàn)的開源可視化庫涧卵,可以流暢的運(yùn)行在 PC 和移動(dòng)設(shè)備上,兼容當(dāng)前絕大部分瀏覽器腹尖,ECharts 提供了常規(guī)的折線圖柳恐、柱狀圖散點(diǎn)圖热幔、餅圖 等等乐设。

和eharts的區(qū)別

eharts是封裝好的各種的圖表可以直接拿來使用,類似于圖表模具绎巨,直接拿來使用即可近尚。

D3.js就像畫筆一樣,一切都由你自由發(fā)揮场勤。

基本介紹

D3.jsData-Driven Documents)是一個(gè)使用動(dòng)態(tài)圖形進(jìn)行數(shù)據(jù)可視化JavaScript程序庫戈锻。與W3C標(biāo)準(zhǔn)兼容,并且利用廣泛實(shí)現(xiàn)的SVG和媳、JavaScript和CSS標(biāo)準(zhǔn)格遭,改良自早期的Protovis程序庫。與其他的程序庫相比留瞳,D3對(duì)視圖結(jié)果有很大的可控性拒迅。D3是2011年面世的,同年的8月發(fā)布了2.0.0版。到2018年4月璧微,D3已被更新到了5.5.0版[1]作箍。

發(fā)展歷史

在D3.js開發(fā)之前已經(jīng)有出現(xiàn)過許多嘗試做數(shù)據(jù)可視化的包,例如Prefuse前硫,F(xiàn)lare和Protovis程序庫胞得,他們都可以視為D3.js的前身。然而Prefuse和Flare皆有缺點(diǎn)开瞭,皆不能只透過瀏覽器完成渲染懒震,皆須要透過額外插件來完成。

例如2005年發(fā)布的Prefuse是一個(gè)數(shù)據(jù)可視化程序庫嗤详,但是它需要透過網(wǎng)頁的Java插件才能于瀏覽器中呈現(xiàn)个扰;而Flare是2007年發(fā)布的另一個(gè)數(shù)據(jù)可視化工具包,由于其是使用ActionScript編程語言開發(fā)葱色,因此也需要額外插件递宅,即Flash插件才能完成渲染。

2009年苍狰,史丹佛大學(xué)的史丹佛可視化團(tuán)隊(duì)(Stanford Visualization Group)的杰佛瑞·赫爾办龄、邁克·保斯托和瓦迪姆·歐格菲茲齊利用開發(fā)Prefuse和Flare的經(jīng)驗(yàn)開始用Javscript開發(fā)了可從給定數(shù)據(jù)產(chǎn)生SVG圖形的Protovis程序庫。而Protovis程序庫在業(yè)界和學(xué)界皆有一定的知名度[3]淋昭。

2011年俐填,史丹佛可視化團(tuán)隊(duì)停止開發(fā)Protovis,并開始開發(fā)新的數(shù)據(jù)可視化程序庫翔忽,借由之前開發(fā)Protovis的經(jīng)驗(yàn)英融,開發(fā)出了D3.js程序庫,在注重于Web標(biāo)準(zhǔn)的同時(shí)提供了更豐富的平臺(tái)也有了更好的性能[4]歇式。

技術(shù)原理

D3.js透過預(yù)先創(chuàng)建好遷入于網(wǎng)頁中的JavaScript函數(shù)來選擇網(wǎng)頁元素驶悟、創(chuàng)建SVG元素、調(diào)整CSS來呈現(xiàn)數(shù)據(jù)材失,并且也可以設(shè)置動(dòng)畫痕鳍、動(dòng)態(tài)改變對(duì)象狀態(tài)或加入工具提示來完成用戶交互功能。使用簡單的D3.js函數(shù)就能夠?qū)⒋笮偷臄?shù)據(jù)數(shù)據(jù)結(jié)構(gòu)與SVG對(duì)象進(jìn)行綁定龙巨,并且能生成格式化文本和各種圖表笼呆。

基本使用

hello world

先嘗試用 D3 寫第一個(gè) HelloWorld 程序。學(xué)編程入門的第一個(gè)程序都是在屏幕上輸出 HelloWorld旨别,本課稍微有些不同抄邀,不是單純的輸出。

在 HTML 中輸出 HelloWorld 是怎樣的呢昼榛,先看下面的代碼境肾。

<html> 
  <head> 
        <meta charset="utf-8"> 
        <title>HelloWorld</title> 
  </head> 
    <body> 
        <p>Hello World 1</p>
        <p>Hello World 2</p>
    </body> 
</html>
用 JavaScript 來更改 HelloWorld

對(duì)于上面輸出的內(nèi)容剔难,如果想用 JavaScript 來更改這兩行文字,怎么辦呢奥喻?我們會(huì)添加代碼變?yōu)椋?/p>

<html> 
  <head> 
        <meta charset="utf-8"> 
        <title>HelloWorld</title> 
  </head> 
    <body> 
    <p>Hello World 1</p>
    <p>Hello World 2</p>
        <script>
        var paragraphs = document.getElementsByTagName("p");
        for (var i = 0; i < paragraphs.length; i++) {
          var paragraph = paragraphs.item(i);
          paragraph.innerHTML = "I like dog.";
        }          
        </script> 
    </body> 
</html>
用 D3 來更改 HelloWorld

如果使用 D3.js 來修改這兩行呢偶宫?只需添加一行代碼即可。注意不要忘了引用 D3.js 源文件环鲤。

引入

<script src="https://d3js.org/d3.v5.js"></script>
<html> 
  <head> 
        <meta charset="utf-8"> 
        <title>HelloWorld</title> 
  </head> 
    <body> 
        <p>Hello World 1</p>
        <p>Hello World 2</p>
        <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
        <script>  
        d3.select("body").selectAll("p").text("www.ourd3js.com");      
        </script> 
    </body> 
</html>

接下來改變字體的顏色和大小纯趋,稍微修改上述代碼:

//選擇<body>中所有的<p>,其文本內(nèi)容為 www.ourd3js.com冷离,選擇集保存在變量 p 中
var p = d3.select("body")
          .selectAll("p")
          .text("www.ourd3js.com");

//修改段落的顏色和字體大小
p.style("color","red")
 .style("font-size","72px");

選擇元素

在 D3 中吵冒,用于選擇元素的函數(shù)有兩個(gè):

  • d3.select():是選擇所有指定元素的第一個(gè)
  • d3.selectAll():是選擇指定元素的全部

這兩個(gè)函數(shù)返回的結(jié)果稱為選擇集。

例如西剥,選擇集的常見用法如下痹栖。

var body = d3.select("body"); //選擇文檔中的body元素
var p1 = body.select("p");      //選擇body中的第一個(gè)p元素
var p = body.selectAll("p");    //選擇body中的所有p元素
var svg = body.select("svg");   //選擇body中的svg元素
var rects = svg.selectAll("rect");  //選擇svg中所有的svg元素

綁定數(shù)據(jù)

選擇集和綁定數(shù)據(jù)通常是一起使用的。

D3 有一個(gè)很獨(dú)特的功能:能將數(shù)據(jù)綁定到 DOM 上瞭空,也就是綁定到文檔上揪阿。這么說可能不好理解,例如網(wǎng)頁中有段落元素 p 和一個(gè)整數(shù) 5咆畏,于是可以將整數(shù) 5 與 p 綁定到一起南捂。綁定之后,當(dāng)需要依靠這個(gè)數(shù)據(jù)才操作元素的時(shí)候旧找,會(huì)很方便溺健。

D3 中是通過以下兩個(gè)函數(shù)來綁定數(shù)據(jù)的:

  • datum():綁定一個(gè)數(shù)據(jù)到選擇集上
  • data():綁定一個(gè)數(shù)組到選擇集上,數(shù)組的各項(xiàng)值分別與選擇集的各元素綁定

相對(duì)而言钮蛛,data() 比較常用鞭缭。

假設(shè)現(xiàn)在有三個(gè)段落元素如下。

<p>Apple</p>
<p>Pear</p>
<p>Banana</p>

datum()

假設(shè)有一字符串 China愿卒,要將此字符串分別與三個(gè)段落元素綁定缚去,代碼如下:

var str = "China";

var body = d3.select("body");
var p = body.selectAll("p");

p.datum(str);

p.text(function(d, i){
    return "第 "+ i + " 個(gè)元素綁定的數(shù)據(jù)是 " + d;
});

綁定數(shù)據(jù)后潮秘,使用此數(shù)據(jù)來修改三個(gè)段落元素的內(nèi)容琼开,其結(jié)果如下:

第 0 個(gè)元素綁定的數(shù)據(jù)是 China

第 1 個(gè)元素綁定的數(shù)據(jù)是 China

第 2 個(gè)元素綁定的數(shù)據(jù)是 China

在上面的代碼中,用到了一個(gè)無名函數(shù) function(d, i)枕荞。當(dāng)選擇集需要使用被綁定的數(shù)據(jù)時(shí)柜候,常需要這么使用。其包含兩個(gè)參數(shù)躏精,其中:

  • d 代表數(shù)據(jù)渣刷,也就是與某元素綁定的數(shù)據(jù)。
  • i 代表索引矗烛,代表數(shù)據(jù)的索引號(hào)辅柴,從 0 開始。

例如,上述例子中:第 0 個(gè)元素 apple 綁定的數(shù)據(jù)是 China碌嘀。

data()

有一個(gè)數(shù)組涣旨,接下來要分別將數(shù)組的各元素綁定到三個(gè)段落元素上。

var dataset = ["I like dog","I like cat","I like snake"];

綁定之后股冗,其對(duì)應(yīng)關(guān)系的要求為:

  • Apple 與 I like dog 綁定
  • Pear 與 I like cat 綁定
  • Banana 與 I like snake 綁定

調(diào)用 data() 綁定數(shù)據(jù)霹陡,并替換三個(gè)段落元素的字符串為被綁定的字符串,代碼如下:

var body = d3.select("body");
var p = body.selectAll("p");

p.data(dataset)
  .text(function(d, i){
      return d;
});

這段代碼也用到了一個(gè)無名函數(shù) function(d, i)止状,其對(duì)應(yīng)的情況如下:

  • 當(dāng) i == 0 時(shí)烹棉, d 為 I like dog。
  • 當(dāng) i == 1 時(shí)怯疤, d 為 I like cat浆洗。
  • 當(dāng) i == 2 時(shí), d 為 I like snake旅薄。

此時(shí)辅髓,三個(gè)段落元素與數(shù)組 dataset 的三個(gè)字符串是一一對(duì)應(yīng)的,因此少梁,在函數(shù) function(d, i) 直接 return d 即可洛口。

結(jié)果自然是三個(gè)段落的文字分別變成了數(shù)組的三個(gè)字符串。

I like dog

I like cat

I like snake

選擇凯沪、插入第焰、刪除元素

已經(jīng)講解了 select 和 selectAll,以及選擇集的概念妨马。本節(jié)具體講解這兩個(gè)函數(shù)的用法挺举。

假設(shè)在 body 中有三個(gè)段落元素:

<p>Apple</p>
<p>Pear</p>
<p>Banana</p>

現(xiàn)在,要分別完成以下四種選擇元素的任務(wù)烘跺。

選擇第一個(gè) p 元素
t("p");
p1.style("color","red");
選擇三個(gè) p 元素
var p = body.selectAll("p");
p.style("color","red");
選擇第二個(gè) p 元素

有不少方法湘纵,一種比較簡單的是給第二個(gè)元素添加一個(gè) id 號(hào)。

Pear

然后滤淳,使用 select 選擇元素梧喷,注意參數(shù)中 id 名稱前要加 # 號(hào)。

var p2 = body.select("#myid");
p2.style("color","red");
選擇后兩個(gè) p 元素

給后兩個(gè)元素添加 class脖咐,

<p class="myclass">Pear</p>
<p class="myclass">Banana</p>

由于需要選擇多個(gè)元素铺敌,要用 selectAll。注意參數(shù)屁擅,class 名稱前要加一個(gè)點(diǎn)偿凭。

var p = body.selectAll(".myclass");
p.style("color","red");

插入元素

插入元素涉及的函數(shù)有兩個(gè):

  • append():在選擇集末尾插入元素
  • insert():在選擇集前面插入元素

假設(shè)有三個(gè)段落元素,與上文相同派歌。

append()

body.append("p")
    .text("append p element");

在 body 的末尾添加一個(gè) p 元素弯囊,結(jié)果為:

Apple
Pear
Banana
append p element

insert()

在 body 中 id 為 myid 的元素前添加一個(gè)段落元素痰哨。

body.insert("p","#myid")
  .text("insert p element");

已經(jīng)指定了 Pear 段落的 id 為 myid,因此結(jié)果如下匾嘱。

Apple
insert p element
Pear
Banana

刪除元素

刪除一個(gè)元素時(shí)作谭,對(duì)于選擇的元素,使用 remove 即可奄毡,例如:

var p = body.select("#myid");
p.remove();

SVG 基本使用

SVG 意為可縮放矢量圖形(Scalable Vector Graphics)折欠。

SVG 使用 XML 格式定義圖像。

什么是svg

  • SVG 指可伸縮矢量圖形 (Scalable Vector Graphics)
  • SVG 用來定義用于網(wǎng)絡(luò)的基于矢量的圖形
  • SVG 使用 XML 格式定義圖形
  • SVG 圖像在放大或改變尺寸的情況下其圖形質(zhì)量不會(huì)有所損失
  • SVG 是萬維網(wǎng)聯(lián)盟的標(biāo)準(zhǔn)
  • SVG 與諸如 DOM 和 XSL 之類的 W3C 標(biāo)準(zhǔn)是一個(gè)整體

hello-world

<html>
<body>
 
<h1>My first SVG</h1>
 
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <circle cx="100" cy="50" r="40" stroke="black"
  stroke-width="2" fill="red" />
</svg>
 
</body>
</html>

簡單的 SVG 實(shí)例

一個(gè)簡單的SVG圖形例子:

這里是SVG文件(SVG文件的保存與SVG擴(kuò)展):

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <circle cx="100" cy="50" r="40" stroke="black"
  stroke-width="2" fill="red" />
</svg>

第一行包含了 XML 聲明吼过。請(qǐng)注意 standalone 屬性锐秦!該屬性規(guī)定此 SVG 文件是否是"獨(dú)立的",或含有對(duì)外部文件的引用盗忱。

standalone="no" 意味著 SVG 文檔會(huì)引用一個(gè)外部文件 - 在這里酱床,是 DTD 文件。

第二和第三行引用了這個(gè)外部的 SVG DTD趟佃。該 DTD 位于 "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"扇谣。該 DTD 位于 W3C,含有所有允許的 SVG 元素闲昭。

SVG 代碼以 <svg>元素開始罐寨,包括開啟標(biāo)簽<svg>和關(guān)閉標(biāo)簽 </svg>。這是根元素序矩。width 和 height 屬性可設(shè)置此 SVG 文檔的寬度和高度鸯绿。version 屬性可定義所使用的 SVG 版本,xmlns 屬性可定義 SVG 命名空間簸淀。

SVG 的<circle> 用來創(chuàng)建一個(gè)圓瓶蝴。cx 和 cy 屬性定義圓中心的 x 和 y 坐標(biāo)。如果忽略這兩個(gè)屬性租幕,那么圓點(diǎn)會(huì)被設(shè)置為 (0, 0)舷手。r 屬性定義圓的半徑。

stroke 和 stroke-width 屬性控制如何顯示形狀的輪廓劲绪。我們把圓的輪廓設(shè)置為 2px 寬男窟,黑邊框。

fill 屬性設(shè)置形狀內(nèi)的顏色珠叔。我們把填充顏色設(shè)置為紅色蝎宇。

關(guān)閉標(biāo)簽的作用是關(guān)閉 SVG 元素和文檔本身弟劲。

SVG 在 HTML 頁面

SVG 文件可通過以下標(biāo)簽嵌入 HTML 文檔:<embed>祷安、<object> 或者 <iframe>。

SVG的代碼可以直接嵌入到HTML頁面中兔乞,或您可以直接鏈接到SVG文件汇鞭。

使用 <embed> 標(biāo)簽
<embed src="circle1.svg" type="image/svg+xml" />
直接在HTML嵌入SVG代碼

在Firefox凉唐、Internet Explorer9、谷歌Chrome和Safari中霍骄,你可以直接在HTML嵌入SVG代碼台囱。

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
   <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
</svg>

SVG圖形

SVG有一些預(yù)定義的形狀元素,可被開發(fā)者使用和操作:

  • 矩形 <rect>
  • 圓形 <circle>
  • 橢圓 <ellipse>
  • 線 <line>
  • 折線 <polyline>
  • 多邊形 <polygon>
  • 路徑 <path>
矩形

EX1:

<rect> 標(biāo)簽可用來創(chuàng)建矩形读整,以及矩形的變種:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <rect width="300" height="100"
  style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)"/>
</svg>
  • rect 元素的 width 和 height 屬性可定義矩形的高度和寬度
  • style 屬性用來定義 CSS 屬性
  • CSS 的 fill 屬性定義矩形的填充顏色(rgb 值簿训、顏色名或者十六進(jìn)制值)
  • CSS 的 stroke-width 屬性定義矩形邊框的寬度
  • CSS 的 stroke 屬性定義矩形邊框的顏色

EX2:

讓我們看看另一個(gè)例子,它包含一些新的屬性:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <rect x="50" y="20" width="150" height="150"
  style="fill:blue;stroke:pink;stroke-width:5;fill-opacity:0.1;
  stroke-opacity:0.9"/>
</svg>
  • x 屬性定義矩形的左側(cè)位置(例如米间,x="0" 定義矩形到瀏覽器窗口左側(cè)的距離是 0px)
  • y 屬性定義矩形的頂端位置(例如强品,y="0" 定義矩形到瀏覽器窗口頂端的距離是 0px)
  • CSS 的 fill-opacity 屬性定義填充顏色透明度(合法的范圍是:0 - 1)
  • CSS 的 stroke-opacity 屬性定義輪廓顏色的透明度(合法的范圍是:0 - 1)
圓形

<circle> 標(biāo)簽可用來創(chuàng)建一個(gè)圓:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <circle cx="100" cy="50" r="40" stroke="black"
  stroke-width="2" fill="red"/>
</svg>
  • cx和cy屬性定義圓點(diǎn)的x和y坐標(biāo)。如果省略cx和cy屈糊,圓的中心會(huì)被設(shè)置為(0, 0)
  • r屬性定義圓的半徑
橢圓

<ellipse> 元素是用來創(chuàng)建一個(gè)橢圓:

橢圓與圓很相似的榛。不同之處在于橢圓有不同的x和y半徑,而圓的x和y半徑是相同的:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <ellipse cx="300" cy="80" rx="100" ry="50"
  style="fill:yellow;stroke:purple;stroke-width:2"/>
</svg>
直線

<line> 元素是用來創(chuàng)建一個(gè)直線:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <line x1="0" y1="0" x2="200" y2="200"
  style="stroke:rgb(255,0,0);stroke-width:2"/>
</svg>
  • x1 屬性在 x 軸定義線條的開始
  • y1 屬性在 y 軸定義線條的開始
  • x2 屬性在 x 軸定義線條的結(jié)束
  • y2 屬性在 y 軸定義線條的結(jié)束
多邊形

<polygon> 標(biāo)簽用來創(chuàng)建含有不少于三個(gè)邊的圖形逻锐。

polygon來自希臘夫晌。 "Poly" 意味 "many" , "gon" 意味 "angle".

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <polygon points="200,10 250,190 160,210"
  style="fill:lime;stroke:purple;stroke-width:1"/>
</svg>

下面的示例創(chuàng)建一個(gè)四邊的多邊形:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <polygon points="220,10 300,210 170,250 123,234"
  style="fill:lime;stroke:purple;stroke-width:1"/>
</svg>
曲線

<polyline> 元素是用于創(chuàng)建任何只有直線的形狀:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <polyline points="20,20 40,25 60,40 80,120 120,140 200,180"
  style="fill:none;stroke:black;stroke-width:3" />
</svg>

只有直線的另一個(gè)例子:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <polyline points="0,40 40,40 40,80 80,80 80,120 120,120 120,160" style="fill:white;stroke:red;stroke-width:4" />
</svg>
路徑

<path> 元素用于定義一個(gè)路徑昧诱。

下面的命令可用于路徑數(shù)據(jù):

  • M = moveto
  • L = lineto
  • H = horizontal lineto
  • V = vertical lineto
  • C = curveto
  • S = smooth curveto
  • Q = quadratic Bézier curve
  • T = smooth quadratic Bézier curveto
  • A = elliptical Arc
  • Z = closepath

例子定義了一條路徑晓淀,它開始于位置150 0,到達(dá)位置75 200盏档,然后從那里開始到225 200要糊,最后在150 0關(guān)閉路徑。

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <path d="M150 0 L75 200 L225 200 Z" />
</svg>
文本

<text> 元素用于定義文本妆丘。

EX:1

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <text x="0" y="15" fill="red">I love SVG</text>
</svg>
Svg與Canvas的區(qū)別锄俄?
  1. 繪制的圖片格式不同
Canvas 的工具getContext 繪制出來的圖形或傳入的圖片都依賴分辨率,能夠以 .png 和 .jpg格式保存存儲(chǔ)圖像勺拣,可以說是位圖

SVG 可以在H5中直接繪制奶赠,但繪制的是矢量圖

由于位圖依賴分辨率,矢量圖不依賴分辨率药有,所以Canvas和SVG的圖片格式的不同實(shí)際上是他們繪制出來的圖片的格式不同造成的毅戈。
  1. Canvas不支持事件處理器,SVG支持事件處理器
Canvas 繪制的圖像 都在Canvas這個(gè)畫布里面愤惰,是Canvas的一部分苇经,不能用js獲取已經(jīng)繪制好的圖形元素。
  1. 適用范圍不同
Canvas是逐像素進(jìn)行渲染的宦言,一旦圖形繪制完成扇单,就不會(huì)繼續(xù)被瀏覽器關(guān)注。而SVG是通過DOM操作來顯示的奠旺。

所以Canvas的文本渲染能力弱蜘澜,而SVG最適合帶有大型渲染區(qū)域的應(yīng)用程序施流,比如地圖。

而Canvas 最適合有許多對(duì)象要被頻繁重繪的圖形密集型游戲鄙信。

而SVG由于DOM操作 在復(fù)雜度高的游戲應(yīng)用中 會(huì)減慢渲染速度瞪醋。所以不適合在游戲應(yīng)用。

實(shí)踐

做一個(gè)簡單的柱狀圖

image-20190802172953054.png
畫布

前幾章的處理對(duì)象都是 HTML 的文字装诡,沒有涉及圖形的制作银受。

要繪圖,首要需要的是一塊繪圖的“畫布”鸦采。

HTML 5 提供兩種強(qiáng)有力的“畫布”:SVGCanvas蚓土。

添加畫布

D3 雖然沒有明文規(guī)定一定要在 SVG 中繪圖,但是 D3 提供了眾多的 SVG 圖形的生成器赖淤,它們都是只支持 SVG 的蜀漆。因此,建議使用 SVG 畫布咱旱。

使用 D3 在 body 元素中添加 svg 的代碼如下确丢。

var width = 300;  //畫布的寬度
var height = 300;   //畫布的高度

var svg = d3.select("body")     //選擇文檔中的body元素
    .append("svg")          //添加一個(gè)svg元素
    .attr("width", width)       //設(shè)定寬度
    .attr("height", height);    //設(shè)定高度

有了畫布,接下來就可以在畫布上作圖了吐限。

繪制矩形

本文繪制一個(gè)橫向的柱形圖鲜侥。只繪制矩形,不繪制文字和坐標(biāo)軸诸典。

在 SVG 中描函,矩形的元素標(biāo)簽是 rect。例如:

<svg>
  <rect></rect>
  <rect></rect>
</svg>

上面的 rect 里沒有矩形的屬性狐粱。矩形的屬性舀寓,常用的有四個(gè):

  • x:矩形左上角的 x 坐標(biāo)
  • y:矩形左上角的 y 坐標(biāo)
  • width:矩形的寬度
  • height:矩形的高度

要注意,在 SVG 中肌蜻,x 軸的正方向是水平向右互墓,y 軸的正方向是垂直向下的。

現(xiàn)在給出一組數(shù)據(jù)蒋搜,要對(duì)此進(jìn)行可視化篡撵。數(shù)據(jù)如下:

var dataset = [ 250 , 210 , 170 , 130 , 90 ];  //數(shù)據(jù)(表示矩形的寬度)

為簡單起見,我們直接用數(shù)值的大小來表示矩形的像素寬度(后面會(huì)說到這不是一種好方法)豆挽。然后育谬,添加以下代碼。

var rectHeight = 25;   //每個(gè)矩形所占的像素高度(包括空白)

svg.selectAll("rect")
    .data(dataset)
    .enter()
    .append("rect")
    .attr("x",20)
    .attr("y",function(d,i){
         return i * rectHeight;
    })
    .attr("width",function(d){
         return d;
    })
    .attr("height",rectHeight-2)
    .attr("fill","steelblue");

這段代碼添加了與 dataset 數(shù)組的長度相同數(shù)量的矩形帮哈,所使用的語句是:

svg.selectAll("rect")   //選擇svg內(nèi)所有的矩形
    .data(dataset)  //綁定數(shù)組
    .enter()        //指定選擇集的enter部分
    .append("rect") //添加足夠數(shù)量的矩形元素

這段代碼以后會(huì)常常出現(xiàn)在 D3 的代碼中膛檀,請(qǐng)務(wù)必牢記。目前不深入討論它的作用機(jī)制是怎樣的,只需要讀者牢記宿刮,當(dāng):

有數(shù)據(jù),而沒有足夠圖形元素的時(shí)候私蕾,使用此方法可以添加足夠的元素僵缺。

添加了元素之后,就需要分別給各元素的屬性賦值踩叭。在這里用到了 function(d, i)磕潮,前面已經(jīng)講過,d 代表與當(dāng)前元素綁定的數(shù)據(jù)容贝,i 代表索引號(hào)自脯。給屬性賦值的時(shí)候,是需要用到被綁定的數(shù)據(jù)斤富,以及索引號(hào)的膏潮。

最后一行的:

.attr("fill","steelblue");
比例尺的使用

比例尺是 D3 中很重要的一個(gè)概念,上一章里曾經(jīng)提到過直接用數(shù)值的大小來代表像素不是一種好方法满力,本章正是要解決此問題焕参。

為什么需要比例尺

上一章制作了一個(gè)柱形圖,當(dāng)時(shí)有一個(gè)數(shù)組:

var dataset = [ 250 , 210 , 170 , 130 , 90 ];

繪圖時(shí)油额,直接使用 250 給矩形的寬度賦值叠纷,即矩形的寬度就是 250 個(gè)像素。

此方式非常具有局限性潦嘶,如果數(shù)值過大或過小涩嚣,例如:

var dataset_1 = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];
var dataset_2 = [ 2500, 2100, 1700, 1300, 900 ];

對(duì)以上兩個(gè)數(shù)組,絕不可能用 2.5 個(gè)像素來代表矩形的寬度掂僵,那樣根本看不見航厚;也不可能用 2500 個(gè)像素來代表矩形的寬度,因?yàn)楫嫴紱]有那么長锰蓬。

于是阶淘,我們需要一種計(jì)算關(guān)系,能夠:

將某一區(qū)域的值映射到另一區(qū)域互妓,其大小關(guān)系不變溪窒。

這就是比例尺(Scale)。

有哪些比例尺

比例尺冯勉,很像數(shù)學(xué)中的函數(shù)澈蚌。例如,對(duì)于一個(gè)一元二次函數(shù)灼狰,有 x 和 y 兩個(gè)未知數(shù)宛瞄,當(dāng) x 的值確定時(shí),y 的值也就確定了交胚。

在數(shù)學(xué)中份汗,x 的范圍被稱為定義域盈电,y 的范圍被稱為值域

D3 中的比例尺杯活,也有定義域和值域匆帚,分別被稱為 domain 和 range。開發(fā)者需要指定 domain 和 range 的范圍旁钧,如此即可得到一個(gè)計(jì)算關(guān)系吸重。

D3 提供了多種比例尺,下面介紹最常用的兩種歪今。

線性比例尺

線性比例尺嚎幸,能將一個(gè)連續(xù)的區(qū)間,映射到另一區(qū)間寄猩。要解決柱形圖寬度的問題嫉晶,就需要線性比例尺。

假設(shè)有以下數(shù)組:

var dataset = [1.2, 2.3, 0.9, 1.5, 3.3];

現(xiàn)有要求如下:

將 dataset 中最小的值田篇,映射成 0车遂;將最大的值,映射成 300斯辰。

代碼如下:

var min = d3.min(dataset);
var max = d3.max(dataset);

var linear = d3.scaleLinear()
        .domain([min, max])
        .range([0, 300]);

linear(0.9);    //返回 0
linear(2.3);    //返回 175
linear(3.3);    //返回 300

其中舶担,d3.scale.linear() 返回一個(gè)線性比例尺。domain() 和 range() 分別設(shè)定比例尺的定義域和值域彬呻。在這里還用到了兩個(gè)函數(shù)衣陶,它們經(jīng)常與比例尺一起出現(xiàn):

  • d3.max()
  • d3.min()

這兩個(gè)函數(shù)能夠求數(shù)組的最大值和最小值,是 D3 提供的闸氮。按照以上代碼剪况,

比例尺的定義域 domain 為:[0.9, 3.3]

比例尺的值域 range 為:[0, 300]

因此,當(dāng)輸入 0.9 時(shí)蒲跨,返回 0译断;當(dāng)輸入 3.3 時(shí),返回 300或悲。當(dāng)輸入 2.3 時(shí)呢孙咪?返回 175,這是按照線性函數(shù)的規(guī)則計(jì)算的巡语。

有一點(diǎn)請(qǐng)大家記佐岬浮:

d3.scale.linear() 的返回值,是可以當(dāng)做函數(shù)來使用的男公。因此荤堪,才有這樣的用法:linear(0.9)。

序數(shù)比例尺

有時(shí)候,定義域和值域不一定是連續(xù)的澄阳。例如拥知,有兩個(gè)數(shù)組:

var index = [0, 1, 2, 3, 4];
var color = ["red", "blue", "green", "yellow", "black"];

我們希望 0 對(duì)應(yīng)顏色 red,1 對(duì)應(yīng) blue碎赢,依次類推籽孙。

但是怯伊,這些值都是離散的筛婉,線性比例尺不適合却邓,需要用到序數(shù)比例尺镀琉。

var ordinal = d3.scaleOrdinal()
        .domain(index)
        .range(color);

ordinal(0); //返回 red
ordinal(2); //返回 green
ordinal(4); //返回 black
給柱形圖添加比例尺

修改一下數(shù)組峦嗤,再定義一個(gè)線性比例尺。

var dataset = [ 2.5 , 2.1 , 1.7 , 1.3 , 0.9 ];

var linear = d3.scale.linear()
        .domain([0, d3.max(dataset)])
        .range([0, 250]);

其后屋摔,按照上一章的方法添加矩形烁设,在給矩形設(shè)置寬度的時(shí)候,應(yīng)用比例尺钓试。

var rectHeight = 25;   //每個(gè)矩形所占的像素高度(包括空白)

svg.selectAll("rect")
    .data(dataset)
    .enter()
    .append("rect")
    .attr("x",20)
    .attr("y",function(d,i){
         return i * rectHeight;
    })
    .attr("width",function(d){
         return linear(d);   //在這里用比例尺
    })
    .attr("height",rectHeight-2)
    .attr("fill","steelblue");

如此一來装黑,所有的數(shù)值,都按照同一個(gè)線性比例尺的關(guān)系來計(jì)算寬度弓熏,因此數(shù)值之間的大小關(guān)系不變恋谭。

坐標(biāo)軸

坐標(biāo)軸,是可視化圖表中經(jīng)常出現(xiàn)的一種圖形挽鞠,由一些列線段和刻度組成疚颊。坐標(biāo)軸在 SVG 中是沒有現(xiàn)成的圖形元素的,需要用其他的元素組合構(gòu)成信认。D3 提供了坐標(biāo)軸的組件材义,如此在 SVG 畫布中繪制坐標(biāo)軸變得像添加一個(gè)普通元素一樣簡單。

image-20190802180149326.png

在 SVG 畫布的預(yù)定義元素里嫁赏,有六種基本圖形:

  • 矩形
  • 圓形
  • 橢圓
  • 線段
  • 折線
  • 多邊形

另外其掂,還有一種比較特殊,也是功能最強(qiáng)的元素:

  • 路徑

畫布中的所有圖形潦蝇,都是由以上七種元素組成款熬。

顯然,這里面沒有坐標(biāo)軸 這種元素攘乒。如果有的話华烟,我們可以采用類似以下的方式定義:

<axis x1="" x2="" ...></axis>

很可惜,沒有這種元素持灰。但是盔夜,這種設(shè)計(jì)是合理的:不可能為每一種圖形都配備一個(gè)單獨(dú)的元素,那樣 SVG 就會(huì)過于龐大。

因此喂链,我們需要用其他元素來組合成坐標(biāo)軸返十,最終使其變?yōu)轭愃埔韵碌男问剑?/p>

<g>
<!-- 第一個(gè)刻度 -->
<g>
<line></line>   <!-- 第一個(gè)刻度的直線 -->
<text></text>   <!-- 第一個(gè)刻度的文字 -->
</g>
<!-- 第二個(gè)刻度 -->
<g>
<line></line>   <!-- 第二個(gè)刻度的直線 -->
<text></text>   <!-- 第二個(gè)刻度的文字 -->
</g> 
...
<!-- 坐標(biāo)軸的軸線 -->
<path></path>
</g>

分組元素 ,是 SVG 畫布中的元素椭微,意思是 group洞坑。此元素是將其他元素進(jìn)行組合的容器,在這里是用于將坐標(biāo)軸的其他元素分組存放蝇率。

如果需要手動(dòng)添加這些元素就太麻煩了迟杂,為此,D3 提供了一個(gè)組件:d3.svg.axis()本慕。它為我們完成了以上工作排拷。

定義坐標(biāo)軸

上一章提到了比例尺的概念,要生成坐標(biāo)軸锅尘,需要用到比例尺监氢,它們二者經(jīng)常是一起使用的。下面藤违,在上一章的數(shù)據(jù)和比例尺的基礎(chǔ)上浪腐,添加一個(gè)坐標(biāo)軸的組件。

var dataset = [1, 2, 3, 4, 5];  // 數(shù)據(jù)源  x

// 比例尺  讓圖表更加的直觀顿乒, 合理

var min = d3.min(dataset);
var max = d3.max(dataset);

// console.log(max)

// scaleLinear可以定義比例尺  domain range
var linear = d3.scaleLinear().domain([0, max]).range([0, 300]);

// 添加坐標(biāo)軸
var xAxis = d3.axisBottom(linear);
在svg中添加坐標(biāo)軸

定義了坐標(biāo)軸之后议街,只需要在 SVG 中添加一個(gè)分組元素 ,再將坐標(biāo)軸的其他元素添加到這個(gè) 里即可璧榄。代碼如下:

svg.append("g")
   .attr("transform","translate(20,130)")
   .call(axis);

讓你的坐標(biāo)軸動(dòng)起來demo

<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #update {
            position: absolute;
            top: 10px;
            left: 10px;
        }
    </style>
</head>

<body>
    <button id="update">更新</button>
    <script src="https://d3js.org/d3.v5.js"></script>
    <script>

        // 1. 添加畫布
        var width = 960;
        var height = 500;

        var svg = d3.select('body').append('svg').attr('width', width).attr('height', height)

        // 完成 靜態(tài)的  坐標(biāo)軸
        // 線性比例尺
        var scale = d3.scaleLinear().domain([0, 100]).range([100, 860]);

        var axis = d3.axisBottom(scale);

        
        var g = svg.append('g').attr('id', 'g').call(axis);

        // 綁定事件
        d3.select('#update').on('click', function() {
            // 更新數(shù)據(jù)
            scale.domain([0, Math.random() * 100]);
            // g.call(axis);
            // transition d3提供默認(rèn)的動(dòng)畫
            d3.select('#g').transition().call(axis)
        })

    </script>
</body>

</html>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末特漩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子犹菱,更是在濱河造成了極大的恐慌拾稳,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腊脱,死亡現(xiàn)場離奇詭異访得,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)陕凹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門悍抑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人杜耙,你說我怎么就攤上這事搜骡。” “怎么了佑女?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵记靡,是天一觀的道長谈竿。 經(jīng)常有香客問我,道長摸吠,這世上最難降的妖魔是什么空凸? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮寸痢,結(jié)果婚禮上呀洲,老公的妹妹穿的比我還像新娘。我一直安慰自己啼止,他們只是感情好道逗,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著献烦,像睡著了一般滓窍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仿荆,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天贰您,我揣著相機(jī)與錄音坏平,去河邊找鬼拢操。 笑死,一個(gè)胖子當(dāng)著我的面吹牛舶替,可吹牛的內(nèi)容都是我干的令境。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼顾瞪,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼舔庶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起陈醒,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤惕橙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后钉跷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弥鹦,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年爷辙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彬坏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡膝晾,死狀恐怖栓始,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情血当,我是刑警寧澤幻赚,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布禀忆,位于F島的核電站,受9級(jí)特大地震影響落恼,放射性物質(zhì)發(fā)生泄漏油湖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一领跛、第九天 我趴在偏房一處隱蔽的房頂上張望乏德。 院中可真熱鬧,春花似錦吠昭、人聲如沸喊括。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽郑什。三九已至,卻和暖如春蒲肋,著一層夾襖步出監(jiān)牢的瞬間蘑拯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來泰國打工兜粘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留申窘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓孔轴,卻偏偏與公主長得像剃法,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子路鹰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349