可視化工具d3(v5)教程

1、用D3更改Hello World

  <html> 
      <head> 
            <meta charset="utf-8"> 
            <title>HelloWorld</title> 
            <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> 
      </head>
      <body>
           <p>Hello World</p>
           <script>  
           d3.select("body").select("p").text("SWUSTVIS");      
           </script> 
      </body> 
    </html>

輸出結果SWUSTVIS
2、選擇集
在 D3 中,用於選擇元素的函數有兩個,這兩個函數返回的結果稱爲選擇集。
d3.select():選擇所有指定元素的第一個
d3.selectAll():選擇指定全部元素
例如,選擇集的常見用法如下。

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

選擇元素函數後常用鏈式表達接其他操作,如:

d3.select("#id").text("SWUSTVIS").attr("font-size","12px");

3、綁定數據
選擇集和綁定數據通常是一起使用的,D3 中是通過以下兩個函數來綁定數據的:
datum():綁定一個數據到選擇集上
data():綁定一個數組到選擇集上,數組的各項值分別與選擇集的各元素綁定

假設現在有三個段落元素如下:

<p></p>
<p></p>
<p></p>

對於datum():
假設有一字符串 SWUSTVIS,要將此字符串分別與三個段落元素綁定,代碼如下:

var str = "SWUSTVIS";
var body = d3.select("body");
var p = body.selectAll("p");
p.datum(str);
p.text(function(d, i){
    return "第 "+ i + " 個元素綁定的數據是 " + d;
});

綁定數據後,使用此數據來修改三個段落元素的內容,其結果如下:

第 0 個元素綁定的數據是 SWUSTVIS
第 1 個元素綁定的數據是 SWUSTVIS
第 2 個元素綁定的數據是 SWUSTVIS
對於data():
有一個數組,接下來要分別將數組的各元素綁定到三個段落元素上。
I love three things in this world.Sun, moon and you. Sun for morning, moon for night, and you forever.

var dataset = ["sun","moon","you"];

調用 data() 綁定數據,並替換三個段落元素的字符串爲被綁定的字符串,代碼如下:

var body = d3.select("body");
var p = body.selectAll("p");
p.data(dataset)
  .text(function(d, i){
      return "I love " + d;
  });

結果自然是三個段落的文字分別變成了數組的三個字符串。

I love sun
I love moon
I love you

前面代碼也用到了一個無名函數 function(d, i),其對應的情況如下:

d ------- data    數據
i ------- index   索引

當 i == 0 時, d 爲 sun。
當 i == 1 時, d 爲 moon。
當 i == 2 時, d 爲 you。
此時,三個段落元素與數組 dataset 的三個字符串是一一對應的,在函數 function(d, i) 直接 return d 即可。
4、選擇、插入、刪除元素
1.選擇元素
假設在 body 中有三個段落元素:

<p>Sun</p>
<p>Moon</p>
<p>You</p>

現在,要分別完成以下四種選擇元素的任務。
選擇第一個元素

d3.select("body").select("p").style("color","red");

在這裏插入圖片描述
選擇第所有元素

d3.select("body").selectAll("p").style("color","red");

在這裏插入圖片描述
選擇第二個元素
方法很多,一種比較簡單的是給第二個元素添加一個 id 號。

<p id="moon">Moon</p>
d3.select("#moon").style("color","red");

在這裏插入圖片描述
選擇後兩個元素
給後兩個元素添加 class,

<p class="myclass">Moon</p>
<p class="myclass">You</p>

在這裏插入圖片描述
由於需要選擇多個元素,要用 selectAll

d3.selectAll(".myclass").style("color","red");

2.插入元素

插入元素涉及的函數有兩個:

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

假設有三個段落元素,與上文相同。
append()

d3.select("body").append("p").text("Star");

在這裏插入圖片描述
insert()

d3.select("body").insert("p","#moon").text("Star");

在這裏插入圖片描述
3.刪除元素
刪除一個元素時,對於選擇的元素,使用 remove 即可。
remove()

d3.select("#moon").remove();

在這裏插入圖片描述
5、做一個簡單的圖表
柱形圖是一種最簡單的可視化圖標,主要有矩形、文字標籤、座標軸組成。爲簡單起見,只繪製矩形的部分,用以講解如何使用 D3 在 SVG 畫布中繪圖。
畫布是什麼
之前處理對象都是 HTML 的文字,沒有涉及圖形的製作。要繪圖,首要需要的是一塊繪圖的“畫布”。HTML 5 提供兩種強有力的“畫布”:SVG 和 Canvas。
SVG 繪製的是矢量圖,因此對圖像進行放大不會失真,可以爲每個元素添加 JavaScript 事件處理器。每個圖形均視爲對象,更改對象的屬性,圖形也會改變。要注意,在 SVG 中,x 軸的正方向是水平向右,y 軸的正方向是垂直向下的。

在 canvas 中,一旦圖形被繪製完成,它就不會繼續得到瀏覽器的關注。如果其位置發生變化,那麼整個場景也需要重新繪製,包括任何或許已被圖形覆蓋的對象。
添加畫布
D3 雖然沒有明文規定一定要在 SVG 中繪圖,但是 D3 提供了衆多的 SVG 圖形的生成器,它們都是隻支持 SVG 的。因此,建議使用 SVG 畫布。
使用 D3 在 body 元素中添加 svg 的代碼如下。

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

繪製矩形

繪製一個橫向的柱形圖。只繪製矩形,不繪製文字和座標軸。在 SVG 中,矩形的元素標籤是 rect。
例如:

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

矩形的屬性,常用的有四個:

x:矩形左上角的 x 座標
y:矩形左上角的 y 座標
width:矩形的寬度
height:矩形的高度

現在給出一組數據,要對此進行可視化。

var dataset = [ 250 , 210 , 170 , 130 , 90 ];  //數據(表示矩形的寬度)
var width = 300;  //畫布的寬度
var height = 300;   //畫布的高度
var svg = d3.select("body")     //選擇文檔中的body元素
    .append("svg")          //添加一個svg元素
    .attr("width", width)       //設定寬度
    .attr("height", height);    //設定高度    
var rectHeight = 25;   //每個矩形所佔的像素高度(包括空白)
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");

在這裏插入圖片描述
PS:橫向變縱向?

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

在這裏插入圖片描述
PS:上面的例子是值和像素大小是一樣的,那麼就會出現一個問題(引入比例尺)。

6、比例尺的使用
爲什麼需要比例尺
上一章製作了一個柱形圖,當時有一個數組:

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

繪圖時,直接使用 250 給矩形的寬度賦值,即矩形的寬度就是 250 個像素。此方式非常具有侷限性,如果數值過大或過小,例如:

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

對以上兩個數組,絕不可能用 2.5 個像素來代表矩形的寬度,那樣根本看不見;也不可能用 2500 個像素來代表矩形的寬度,因爲畫布沒有那麼長。於是,我們需要一種計算關係,能夠將某一區域的值映射到另一區域,其大小關係不變,這就是比例尺(Scale)。
有哪些比例尺
比例尺,很像數學中的函數。例如,對於一個一元二次函數,有 x 和 y 兩個未知數,當 x 的值確定時,y 的值也就確定了。在數學中,x 的範圍被稱爲定義域,y 的範圍被稱爲值域。D3 中的比例尺,也有定義域和值域,分別被稱爲 domain 和 range。開發者需要指定 domain 和 range 的範圍,如此即可得到一個計算關係。
D3 提供了多種比例尺,下面介紹最常用的兩種。
1)線性比例尺
線性比例尺,能將一個連續的區間,映射到另一區間。要解決柱形圖寬度的問題,就需要線性比例尺。假設有以下數組:

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

現有要求如下:
將 dataset 中最小的值,映射成 0;將最大的值,映射成 300。代碼如下:

var min = d3.min(dataset);
var max = d3.max(dataset);
var linear = d3.scale.linear()
        .domain([min, max])     //設置比例尺的定義域
        .range([0, 300]);     //設置比例尺的值域
linear(0.9);    //返回 0
linear(2.3);    //返回 175
linear(3.3);    //返回 300

其中,d3.scale.linear() 返回一個線性比例尺。domain() 和 range() 分別設定比例尺的定義域和值域。在這裏還用到了兩個函數,它們經常與比例尺一起出現:
在v4、v5中,d3.scale.linear() 應該寫爲d3.scaleLinear()

d3.max()
d3.min()

這兩個函數能夠求數組的最大值和最小值,是 D3 提供的。按照以上代碼,
比例尺的定義域 domain 爲:[0.9, 3.3]
比例尺的值域 range 爲:[0, 300]
因此,當輸入 0.9 時,返回 0;當輸入 3.3 時,返回 300。當輸入 2.3 時呢?返回 175,這是按照線性函數的規則計算的。有一點請大家記住:d3.scale.linear() 是可以當做函數來使用的,纔有這樣的用法:linear(0.9)。
2)序數比例尺
有時候,定義域和值域不一定是連續的。例如,有兩個數組:

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

我們希望 0 對應顏色 red,1 對應 blue,依次類推。
但是,這些值都是離散的,線性比例尺不適合,需要用到序數比例尺。

var ordinal = d3.scale.ordinal()
        .domain(index)
        .range(color);
ordinal(0); //返回 red
ordinal(2); //返回 green
ordinal(4); //返回 black

用法與線性比例尺是類似的。
給柱形圖添加比例尺
在上一章的基礎上,修改一下數組,再定義一個線性比例尺。

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]);
var rectHeight = 25;   //每個矩形所佔的像素高度(包括空白)
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");

如此一來,所有的數值,都按照同一個線性比例尺的關係來計算寬度,因此數值之間的大小關係不變。
7、座標軸
座標軸,是可視化圖表中經常出現的一種圖形,由一些列線段和刻度組成。座標軸在 SVG 中是沒有現成的圖形元素的,需要用其他的元素組合構成。D3 提供了座標軸的組件,如此在 SVG 畫布中繪製座標軸變得像添加一個普通元素一樣簡單。
定義座標軸
上一章提到了比例尺的概念,要生成座標軸,需要用到比例尺,它們二者經常是一起使用的。下面,在上一章的數據和比例尺的基礎上,添加一個座標軸的組件。

//數據
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]);
var axis = d3.svg.axis() //座標軸組件
     .scale(linear)      //指定比例尺
     .orient("bottom")   //指定刻度的方向
     .ticks(7);          //指定刻度的數量

在 SVG 中添加座標軸
定義了座標軸之後,只需要在 SVG 中添加一個分組元素 ,再將座標軸的其他元素添加到組裏即可。代碼如下:

svg.append("g").call(axis);

在這裏插入圖片描述
設定座標軸的樣式和位置
默認的座標軸樣式不太美觀,下面提供一個常見的樣式:

<style>
.axis path,
.axis line{
    fill: none;
    stroke: black;
    shape-rendering: crispEdges;
}
.axis text {
    font-family: sans-serif;
    font-size: 11px;
}
</style>

分別定義了類 axis 下的 path、line、text 元素的樣式。接下來,只需要將座標軸的類設定爲 axis 即可。
座標軸的位置,可以通過 transform 屬性來設定。
通常在添加元素的時候就一併設定,寫成如下形式:

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

在這裏插入圖片描述
8、完整的柱形圖
一個完整的柱形圖包含三部分:矩形、文字、座標軸。本章將對前幾章的內容進行綜合的運用,製作一個實用的柱形圖,內容包括:選擇集、數據綁定、比例尺、座標軸等內容。
在這裏插入圖片描述
添加 SVG 畫布

//畫布大小
var width = 400;
var height = 400;
//在 body 裏添加一個 SVG 畫布   
var svg = d3.select("body")
    .append("svg")
    .attr("width", width)
    .attr("height", height);
//畫布周邊的空白
 var padding = {left:30, right:30, top:20, bottom:20};

定義數據和比例尺

//定義一個數組
var dataset = [10, 20, 30, 40, 33, 24, 12, 5];
//x軸的比例尺
var xScale = d3.scaleLinear()
    .domain([0,dataset.length])
    .range([0, 340]);

//y軸的比例尺
var yScale = d3.scaleLinear()
    .domain([0,d3.max(dataset)])
    .range([height - padding.top - padding.bottom, 0]);

定義座標軸

 //定義x軸
 var xAxis = d3.axisBottom()
      .scale(xScale)   //指定比例尺
      // .orient("bottom");
      // .ticks(7);   //指定刻度的數量

  //定義y軸
  var yAxis = d3.axisLeft()
      .scale(yScale)
      // .orient("left");

添加矩形和文字元素

//矩形之間的空白
var rectPadding = 4;

//添加矩形元素
var rects = svg.selectAll(".MyRect")
                .data(dataset)
                .enter()
                .append("rect")
                .attr("class","MyRect")
                .attr("transform","translate(" + padding.left + "," + padding.top + ")")
                .attr("x", function(d,i){   
                    return 35+i*40;
                } )
                .attr("y",function(d){     
                    return yScale(d);
                })
                //矩形的寬
                .attr("width", 25)
                .attr("height", function(d){
                    return height - padding.top - padding.bottom - yScale(d);
                })
                .attr("fill","steelblue");

//添加文字元素
        var texts = svg.selectAll(".MyText")
                .data(dataset)   //填充數據
                .enter()
                .append("text")
                .attr('fill','#fff')   //設置字體顏色
                .attr("class","MyText")
                .attr("transform","translate(" + padding.left + "," + padding.top + ")")
                .attr("x", function(d,i){    //文本x座標
                    return 35+i*40;
                } )
                .attr("y",function(d){   //文本y座標
                    return yScale(d);
                })
                .attr("dx",function(){       //文本水平平移
                    return 4;
                })
                .attr("dy",function(d){    //文本垂直平移
                    return 20;
                })
                .text(function(d){
                    return d;
                })			  

添加座標軸的元素

//添加x軸
svg.append("g")
  .attr("class","axis")
  .attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
  .call(xAxis); 
//添加y軸
svg.append("g")
  .attr("class","axis")
  .attr("transform","translate(" + padding.left + "," + padding.top + ")")
  .call(yAxis);

在這裏插入圖片描述
9、讓圖表動起來
D3 支持製作動態的圖表。有時候,圖表的變化需要緩慢的發生,以便於讓用戶看清楚變化的過程。
什麼是動態效果
前面幾章製作的圖表是一蹴而就地出現,然後繪製完成後不再發生變化的,這是靜態的圖表。動態的圖表,是指圖表在某一時間段會發生某種變化,可能是形狀、顏色、位置等,而且用戶是可以看到變化的過程的。
例如,有一個圓,圓心爲 (100, 100)。現在我們希望圓的 x 座標從 100 移到 300,並且移動過程在 2 秒的時間內發生。這種時候就需要用到動態效果,在 D3 裏我們稱之爲過渡(transition)。
實現動態的方法
D3 提供了 4 個方法用於實現圖形的過渡:從狀態 A 變爲狀態 B。

1)transition() 啓動過渡效果
其前後是圖形變化前後的狀態(形狀、位置、顏色等等),例如:

.attr("fill","red")         //初始顏色爲紅色
.transition()               //啓動過渡
.attr("fill","steelblue")   //終止顏色爲鐵藍色

D3 會自動對兩種顏色(紅色和鐵藍色)之間的顏色值(RGB值)進行插值計算,得到過渡用的顏色值。我們無需知道中間是怎麼計算的,只需要享受結果即可。

2)duration() 指定過渡的持續時間,單位爲毫秒。
如 duration(2000) ,指持續 2000 毫秒,即 2 秒。

3)ease()指定過渡的方式,常用的有:
.ease(d3.easeElasticInOut)表示過渡方式
d3.easeLinear(t) Linear(線性) 緩動
d3.easePolyOut(t) 反轉 polynomial 緩動;
d3.easeQuadInOut(t) Symmetric cubic(對稱三次緩動)
d3.easeSinIn(t) Sinusoidal(正弦緩動); 返回 sin(t).
d3.easeBounceIn(t) Bounce(彈跳緩動), 就像是一個橡皮球.
d3.easeBounce(t) d3.easeBounceOut(t)反轉 Bounce(彈跳緩動)
ease過渡方法
4)delay() 指定延遲的時間,表示一定時間後纔開始轉變,此函數可以對整體指定延遲,也可以對個別指定延遲。
例如,對整體指定時:

.transition()
.duration(1000)
.delay(500)

如此,圖形整體在延遲 500 毫秒後發生變化,變化的時長爲 1000 毫秒。因此,過渡的總時長爲1500毫秒。
又如,對一個一個的圖形(圖形上綁定了數據)進行指定時:

.transition()
.duration(1000)
.delay(funtion(d,i){
    return 200*i;
})

如此,假設有 10 個元素,那麼第 1 個元素延遲 0 毫秒(因爲 i = 0),第 2 個元素延遲 200 毫秒,第 3 個延遲 400 毫秒,依次類推….整個過渡的長度爲 200 * 9 + 1000 = 2800 毫秒。

實現簡單的動態效果

下面將在 SVG 畫布裏添加三個圓,圓出現之後,立即啓動過渡效果。

<template>
    <div class="circles">
        
    </div>
</template>
<script>
import * as d3 from "d3"
import { delay } from 'q';
export default {
    mounted(){
        //畫布大小
        var width = 600;
        var height =600;

        //在 body 裏添加一個 SVG 畫布   
        var svg = d3.select(".circles")
        // var svg=scale.select('svg')
            .append("svg")
            .attr("width", width)
            .attr("height", height)
            .attr('background','#ededed');

        //畫布周邊的空白
        var padding = {left:30, right:30, top:20, bottom:20};
        //第一個圓
        var circle1=svg.append('circle')   //添加圓
                    .attr('cx',100)      //圓心橫座標
                    .attr('cy',100)     //圓心縱座標
                    .attr('r',50)       //圓的半徑
                    .attr('fill','skyblue');      //圓的顏色
        circle1.transition()       //啓動動畫,在3秒內平移到500處
                .duration(3000)
                .attr('cx',500);
        //第二個圓
        var circle2=svg.append('circle')
            .attr('cx',100)
            .attr('cy',200)
            .attr('r',50)
            .attr('fill','yellow');
        circle2.transition()     //啓動動畫,在2秒內顏色變爲藍色,半徑變爲30
                .duration(2000)
                // .delay(2000)
                .attr('fill','blue')
                .attr('r',30)

        //第三個圓
        var circle3=svg.append('circle')    
            .attr('cx',100)
            .attr('cy',400)
            .attr('r',50)
            .attr('fill','red');
        circle3.transition()    //啓動動畫,在3秒內顏色變爲藍色#333,半徑變爲60,平移到500處
                .duration(3000)
                .ease(d3.easeElasticInOut)
                .attr('cx',500)      
                .style('fill','#333')
                .attr('r',60)
    }
}
</script>

給柱形圖加上動態效果

在上一章完整柱形圖的基礎上稍作修改,即可做成一個帶動態效果的、有意思的柱形圖。在添加文字元素和矩形元素的時候,啓動過渡效果,讓各柱形和文字緩慢升至目標高度,並且在目標處跳動幾次。
對於文字元素,代碼如下:

var texts = svg.selectAll(".MyText")
                .data(dataset)   //填充數據
                .enter()
                .append("text")
                .attr('fill','#fff')   //設置字體顏色
                .attr("class","MyText")
                .attr("transform","translate(" + padding.left + "," + padding.top + ")")
                .attr("x", function(d,i){    //文本x座標
                    return 35+i*40;
                } )
                .attr("y",function(d){     //文本y座標
                    var min=yScale.domain()[0];  
                    return yScale(min);
                })
                .transition()   //啓動動畫
                .delay(function(d,i){     //延遲時間
                    return i*200
                })
                .duration(3000)   //持續時間
                .ease(d3.easeBounce)   //過渡方式
                .attr('y',function(d){
                    return yScale(d);
                })
                .attr("dx",function(){       //文本水平平移
                    return 4;
                })
                .attr("dy",function(d){    //文本垂直平移
                    return 20;
                })
                .text(function(d){
                    return d;
                })			 

文字元素的過渡前後,發生變化的是 y 座標。其起始狀態是在 y 軸等於 0 的位置(但要注意,不能在起始狀態直接返回 0,要應用比例尺計算畫布中的位置)。終止狀態是目標值。
10、理解update()、enter()、exit()
Update、Enter、Exit 是 D3 中三個非常重要的概念,它處理的是當選擇集和數據的數量關係不確定的情況。
前幾章裏,反覆出現了形如以下的代碼。

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

update()
當對應的元素正好滿足時 ( 綁定數據數量 = 對應元素 ),實際上並不存在這樣一個函數,只是爲了要與之後的 enter 和 exit 一起說明纔想象有這樣一個函數。但對應元素正好滿足時,直接操作即可,後面直接跟 text ,style 等操作即可。
enter()
當對應的元素不足時 ( 綁定數據數量 > 對應元素 ),當對應的元素不足時,通常要添加元素,使之與綁定數據的數量相等。後面通常先跟 append 操作。
exit()
當對應的元素過多時 ( 綁定數據數量 < 對應元素 ),當對應的元素過多時,通常要刪除元素,使之與綁定數據的數量相等。後面通常要跟 remove 操作。
在這裏插入圖片描述
Update與Enter的使用

<template>
    <div class="upd">
        <p></p>
        <p></p>
        <p></p>
    </div>
</template>
<script>
import * as d3 from "d3"
export default {
    mounted(){
        
        var dataset=[3,5,16,20,58];
        var p=d3.select('.upd')
                .selectAll('p');
        var update=p.data(dataset)
        var enter=update.enter();
        update.text(function(d,i){
            return "update:" +d +",index:"+i;
        }) 
        var pEnter = enter.append("p")
        pEnter.text(function(d,i){
            return "enter: "+d+",index: "+i;
        })
    }
}
</script>

在這裏插入圖片描述
Update與Exit的使用

<template>
    <div class="upd">
        <p></p>
        <p></p>
        <p></p>
    </div>
</template>
<script>
import * as d3 from "d3"
export default {
    mounted(){
        
        var dataset=[3,5];
        var p=d3.select('.upd')
                .selectAll('p');
        var update=p.data(dataset)
        var exit=update.exit();
        update.text(function(d,i){
            return "update:" +d +",index:"+i;
        }) 
        exit.text(function(d,i){
            return 'exit';
        })
    }
}
</script>

在這裏插入圖片描述
11、交互式操作
與圖表的交互,指在圖形元素上設置一個或多個監聽器,當事件發生時,做出相應的反應。

什麼是交互
交互,指的是用戶輸入了某種指令,程序接受到指令之後必須做出某種響應。對可視化圖表來說,交互能使圖表更加生動,能表現更多內容。例如,拖動圖表中某些圖形、鼠標滑到圖形上出現提示框、用觸屏放大或縮小圖形等等。用戶用於交互的工具一般有三種:鼠標、鍵盤、觸屏。

如何添加交互
對某一元素添加交互操作十分簡單,代碼如下:

var circle = svg.append("circle");
circle.on("click", function(){
    //在這裏添加交互內容
});

這段代碼在 SVG 中添加了一個圓,然後添加了一個監聽器,是通過 on() 添加的。在 D3 中,每一個選擇集都有 on() 函數,用於添加事件監聽器。
on() 的第一個參數是監聽的事件,第二個參數是監聽到事件後響應的內容,第二個參數是一個函數。
鼠標事件:
click:鼠標單擊某元素時,相當於 mousedown 和 mouseup 組合在一起。
mouseover:光標放在某元素上。
mouseout:光標從某元素上移出來時。
mousemove:鼠標被移動的時候。
mousedown:鼠標按鈕被按下。
mouseup:鼠標按鈕被鬆開。
dblclick:鼠標雙擊。
鍵盤事件:
keydown:當用戶按下任意鍵時觸發,按住不放會重複觸發此事件。該事件不會區分字母的大小寫,例如“A”和“a”被視爲一致。
keypress:當用戶按下字符鍵(大小寫字母、數字、加號、等號、回車等)時觸發,按住不放會重複觸發此事件。該事件區分字母的大小寫。
keyup:當用戶釋放鍵時觸發,不區分字母的大小寫。 觸屏常用的事件有三個:
當某個事件被監聽到時,D3 會把當前的事件存到 d3.event 對象,裏面保存了當前事件的各種參數,如果需要監聽到事件後立刻輸出該事件,可以添加一行代碼:

circle.on(“click”, function(){
	console.log(d3.event);
});

帶有交互的柱形圖
將之前的柱形圖部分代碼修改成如下代碼。

 var rects = svg.selectAll(".MyRect")
                .data(dataset)
                .enter()
                .append("rect")
                .attr("class","MyRect")
                .attr("transform","translate(" + padding.left + "," + padding.top + ")")
                .attr("x", function(d,i){   
                    return 35+i*40;
                } )
                .attr("y",function(d){     
                    return yScale(d);
                })
                //矩形的寬
                .attr("width", 25)
                .attr("height", function(d){
                    return height - padding.top - padding.bottom - yScale(d);
                })
                .attr("fill","red")     //鼠標滑過前爲紅色
                .on('mouseover',function(d,i){
                    d3.select(this)     //this指當前元素
                        .attr('fill','yellow');    //鼠標滑過時爲黃色
                })
                .on("mouseout",function(d,i){
                    d3.select(this)
                        .transition()
                        .duration(1000)
                        .attr('fill','steelblue')    //鼠標滑過後爲海藍色
                })

這段代碼添加了鼠標移入(mouseover),鼠標移出(mouseout)兩個事件的監聽器。監聽器函數中都使用了 d3.select(this),表示選擇當前的元素,this 是當前的元素,要改變響應事件的元素時這麼寫就好。

12、佈局
佈局,可以理解成 “製作常見圖形的函數”,有了它製作各種相對複雜的圖表就方便多了。
佈局是什麼
佈局,英文是 Layout。從字面看,可以想到有“決定什麼元素繪製在哪裏”的意思。佈局是 D3 中一個十分重要的概念。D3 與其它很多可視化工具不同,相對來說較底層,對初學者來說不太方便,但是一旦掌握了,就比其他工具更加得心應手。下圖展示了 D3 與其它可視化工具的區別:
在這裏插入圖片描述
如何理解佈局

從上面的圖可以看到,佈局的作用是:將不適合用於繪圖的數據轉換成了適合用於繪圖的數據。因此,爲了便於初學者理解,將佈局的作用解釋成:數據轉換。

佈局有哪些

D3 總共提供了 12 個佈局:餅狀圖(Pie)、力導向圖(Force)、弦圖(Chord)、樹狀圖(Tree)、集羣圖(Cluster)、捆圖(Bundle)、打包圖(Pack)、直方圖(Histogram)、分區圖(Partition)、堆棧圖(Stack)、矩陣樹圖(Treemap)、層級圖(Hierarchy)。

12 個佈局中,層級圖(Hierarchy)不能直接使用。集羣圖、打包圖、分區圖、樹狀圖、矩陣樹圖是由層級圖擴展來的。如此一來,能夠使用的佈局是 11 個(有 5 個是由層級圖擴展而來)。這些佈局的作用都是將某種數據轉換成另一種數據,而轉換後的數據是利於可視化的。

Bundle —捆圖
在這裏插入圖片描述
Chord — 弦圖
在這裏插入圖片描述
Cluster — 集羣圖
在這裏插入圖片描述
Force —力學圖、力導向圖
在這裏插入圖片描述
Histogram —- 直方圖(數據分佈圖)
在這裏插入圖片描述
Pack —- 打包圖
在這裏插入圖片描述
Partition —- 分區圖
在這裏插入圖片描述
Pie —- 餅狀圖
在這裏插入圖片描述
Stack —- 堆棧圖
在這裏插入圖片描述
Tree —- 樹狀圖
在這裏插入圖片描述
Treemap —- 矩陣樹圖

在這裏插入圖片描述
13、餅狀圖
本章製作一個餅狀圖。在佈局的應用中,最簡單的就是餅狀圖,通過本文你將對佈局有一個初步瞭解。

數據

假設有如下數據,需要可視化:

var dataset = [ 30 , 10 , 43 , 55 , 13 ];

這樣的值是不能直接繪圖的。例如繪製餅狀圖的一個部分,需要知道一段弧的起始角度和終止角度,這些值都不存在於數組 dataset 中。因此,需要用到佈局,佈局的作用就是計算出適合於作圖的數據。
佈局(數據轉換)
定義一個佈局,d3.pie(),餅狀圖生成器

var pie = d3.pie();

返回值賦給變量 pie,此時 pie 可以當做函數使用。

var piedata = pie(dataset);

將數組 dataset 作爲 pie() 的參數,返回值給 piedata。如此一來,piedata 就是轉換後的數據。

繪製圖形

爲了根據轉換後的數據 piedata 來作圖,還需要一樣工具:生成器。SVG 有一個元素,叫做路徑 path,是 SVG 中功能最強的元素,它可以表示其它任意的圖形。顧名思義,路徑元素就是通過定義一個段“路徑”,來繪製出各種圖形。但是,路徑是很難計算的,通過佈局轉換後的數據 piedata 仍然很難手動計算得到路徑值。爲我們完成這項任務的,就是生成器。
這裏要用到的叫做弧生成器 d3.arc( {} ),能夠生成弧的路徑,因爲餅圖的每一部分都是一段弧。

var outerRadius = 150; //外半徑
var innerRadius = 100; //內半徑,爲0則中間沒有空白

var arc = d3.svg.arc()  //弧生成器
    .innerRadius(innerRadius)   //設置內半徑
    .outerRadius(outerRadius);  //設置外半徑

弧生成器返回的結果賦值給 arc。此時,arc 可以當做一個函數使用,把 piedata 作爲參數傳入,即可得到路徑值。接下來,可以在 SVG 中添加圖形元素了。先在 svg 裏添加足夠數量(5個)個分組元素(g),每一個分組用於存放一段弧的相關元素。

var arcs = svg.selectAll("g")
    .data(piedata)
    .enter()
    .append("g")
    .attr("transform","translate("+ (width/2) +","+ (width/2) +")");

接下來對每個 g 元素,添加 path 。

arcs.append("path")
    .attr("fill",function(d,i){
        return color(i);
    })
    .attr("d",function(d){
        return arc(d);   //調用弧生成器,得到路徑值
    });

因爲 arcs 是同時選擇了 5 個 g 元素的選擇集,所以調用 append(“path”) 後,每個 g 中都有 path 。路徑值的屬性名稱是 d,調用弧生成器後返回的值賦值給它。要注意,arc(d) 的參數 d 是被綁定的數據。
另外,color 是一個顏色比例尺,它能根據傳入的索引號獲取相應的顏色值,定義如下。

var color = d3.scale.category10();   //有十種顏色的顏色比例尺

然後在每一個弧線中心添加文本。

arcs.append("text")
    .attr("transform",function(d){
        return "translate(" + arc.centroid(d) + ")";
    })
    .attr("text-anchor","middle")
    .text(function(d){
        return d.data;
    });

arc.centroid(d) 能算出弧線的中心。要注意,text() 裏返回的是 d.data ,而不是 d 。因爲被綁定的數據是對象,裏面有 d.startAngle、d.endAngle、d.data 等,其中 d.data 纔是轉換前的整數的值。

<template>
    <div class="pies">

    </div>
</template>
<script>
import * as d3 from "d3"
export default {
    mounted(){
        var width=400;
        var height=400;
        var marge = {top:60,bottom:60,left:60,right:60}
        var svg=d3.select('.pies')
                .append('svg')
                .attr('width',width)
                .attr('height',height);
        var g = svg.append("g")
            .attr("transform","translate("+marge.top+","+marge.left+")");
            
        var dataset=[30,10,43,55,13];
        //設置顏色比例尺
        var colorScale=d3.scaleOrdinal()
                    .domain(d3.range(dataset.length))
                    .range(d3.schemeCategory10);

        //新建一個餅狀圖            
        var pie=d3.pie();
        //新建一個弧生成器
        var innerRadius=50;   //內半徑(爲0則中間沒有空白)
        var outerRadius=100;     //外半徑
        var arc_generator = d3.arc()     //弧生成器
                .innerRadius(50)
                .outerRadius(100);
                
        //將原始數據變成可以繪製餅狀圖的數據
        var piedata=pie(dataset);
        console.log(piedata)

        //開始繪製,先爲每個扇形及對應文字建立分組
        var gs=g.selectAll(".g")
                .data(piedata)
                .enter()
                .append("g")
                .attr("transform","translate("+width/2+","+height/2+")")

        //繪製餅狀圖的各個扇形
    	gs.append("path")
    		.attr("d",function(d){
    			return arc_generator(d);     //往弧形生成器中出入數據
    		})
    		.attr("fill",function(d,i){
    			return colorScale(i);     //設置顏色
            });
            
        //繪製餅狀圖上面的文字信息
    	gs.append("text")
    		.attr("transform",function(d){//位置設在中心處
    			return "translate("+arc_generator.centroid(d)+")";
    		})
    		.attr("text-anchor","middle")
    		.text(function(d){
    			return d.data;
    		})
    }
}
</script>

在這裏插入圖片描述
14、力導向圖
力導向圖(Force-Directed Graph),是繪圖的一種算法。在二維或三維空間裏配置節點,節點之間用線連接,稱爲連線。各連線的長度幾乎相等,且儘可能不相交。節點和連線都被施加了力的作用,力是根據節點和連線的相對位置計算的。根據力的作用,來計算節點和連線的運動軌跡,並不斷降低它們的能量,最終達到一種能量很低的安定狀態。
在這裏插入圖片描述
數據
初始數據如下:

var nodes = [ { name: "桂林" }, { name: "廣州" },
              { name: "廈門" }, { name: "杭州" },
              { name: "上海" }, { name: "青島" },
              { name: "天津" } ];
var edges = [ { source : 0 , target: 1 } , { source : 0 , target: 2 } ,
              { source : 0 , target: 3 } , { source : 1 , target: 4 } ,
              { source : 1 , target: 5 } , { source : 1 , target: 6 } ];

佈局(數據轉換)

定義一個力導向圖的佈局如下。

var force = d3.layout.force()
      .nodes(nodes)      //指定節點數組
      .links(edges)      //指定連線數組
      .size([width,height])     //指定作用域範圍
      .linkDistance(150)    //指定連線長度
      .charge([-400]);     //相互之間的作用力

然後,使力學作用生效:

force.start();    //開始作用

繪製

有了轉換後的數據,就可以作圖了。分別繪製三種圖形元素:
line,線段,表示連線。
circle,圓,表示節點。
text,文字,描述節點。

代碼如下:

//添加連線 
 var svg_edges = svg.selectAll("line")
     .data(edges)
     .enter()
     .append("line")
     .style("stroke","#ccc")
     .style("stroke-width",1);

 var color = d3.scale.category20();

 //添加節點 
 var svg_nodes = svg.selectAll("circle")
     .data(nodes)
     .enter()
     .append("circle")
     .attr("r",20)
     .style("fill",function(d,i){
         return color(i);
     })
     .call(force.drag);  //使得節點能夠拖動

 //添加描述節點的文字
 var svg_texts = svg.selectAll("text")
     .data(nodes)
     .enter()
     .append("text")
     .style("fill", "black")
     .attr("dx", 20)
     .attr("dy", 8)
     .text(function(d){
        return d.name;
     });

調用 call( force.drag ) 後節點可被拖動。force.drag() 是一個函數,將其作爲 call() 的參數,相當於將當前選擇的元素傳到 force.drag() 函數中。
最後,還有一段最重要的代碼。由於力導向圖是不斷運動的,每一時刻都在發生更新,因此,必須不斷更新節點和連線的位置。力導向圖佈局 force 有一個事件 tick,每進行到一個時刻,都要調用它,更新的內容就寫在它的監聽器裏就好。

force.on("tick", function(){ //對於每一個時間間隔
    //更新連線座標
    svg_edges.attr("x1",function(d){ return d.source.x; })
        .attr("y1",function(d){ return d.source.y; })
        .attr("x2",function(d){ return d.target.x; })
        .attr("y2",function(d){ return d.target.y; });

    //更新節點座標
    svg_nodes.attr("cx",function(d){ return d.x; })
        .attr("cy",function(d){ return d.y; });

    //更新文字座標
    svg_texts.attr("x", function(d){ return d.x; })
       .attr("y", function(d){ return d.y; });
 });
<template>
    <div class="force">

    </div>
</template>
<script>
import * as d3 from "d3"
export default {
    mounted(){
        var width=600;
        var height=500;
        var marge = {top:60,bottom:60,left:60,right:60}
        var svg=d3.select('.force')
                .append('svg')
                .attr('width',width)
                .attr('height',height);
        var g = svg.append("g")
            .attr("transform","translate("+marge.top+","+marge.left+")");
            
        //準備數據
    	var nodes = [//節點集
    		{name:"湖南邵陽"},
    		{name:"山東萊州"},
    		{name:"廣東陽江"},
    		{name:"山東棗莊"},
    		{name:"澤"},
    		{name:"恆"},
    		{name:"鑫"},
    		{name:"明山"},
    		{name:"班長"}
    	];
    	
    	var edges = [//邊集
    		{source:0,target:4,relation:"籍貫",value:1.3},
    		{source:4,target:5,relation:"舍友",value:1},
    		{source:4,target:6,relation:"舍友",value:1},
    		{source:4,target:7,relation:"舍友",value:1},
    		{source:1,target:6,relation:"籍貫",value:2},
    		{source:2,target:5,relation:"籍貫",value:0.9},
    		{source:3,target:7,relation:"籍貫",value:1},
    		{source:5,target:6,relation:"同學",value:1.6},
    		{source:6,target:7,relation:"朋友",value:0.7},
    		{source:6,target:8,relation:"職責",value:2}
    	];
        //設置一個color的顏色比例尺,爲了讓不同的扇形呈現不同的顏色
    	var colorScale = d3.scaleOrdinal()
    		.domain(d3.range(nodes.length))
            .range(d3.schemeCategory10);
        //新建一個力導向圖    
        var forceSimulation = d3.forceSimulation()
    		.force("link",d3.forceLink())
    		.force("charge",d3.forceManyBody())
            .force("center",d3.forceCenter());
        //生成節點數據
    	forceSimulation.nodes(nodes)
            .on("tick",ticked);//這個函數很重要,後面給出具體實現和說明
        //生成邊數據
    	forceSimulation.force("link")
    		.links(edges)
    		.distance(function(d){//每一邊的長度
    			return d.value*100;
            }) 
        //設置圖形的中心位置	
    	forceSimulation.force("center")
    		.x(width/2)
            .y(height/2);
        //在瀏覽器的控制檯輸出
    	console.log(nodes);
        console.log(edges);
        //繪製邊
    	var links = g.append("g")
    		.selectAll("line")
    		.data(edges)
    		.enter()
    		.append("line")
    		.attr("stroke",function(d,i){
    			return colorScale(i);
    		})
    		.attr("stroke-width",1);
        var linksText = g.append("g")
    		.selectAll("text")
    		.data(edges)
    		.enter()
    		.append("text")
    		.text(function(d){
    			return d.relation;
            })
        var gs = g.selectAll(".circleText")
    		.data(nodes)
    		.enter()
    		.append("g")
    		.attr("transform",function(d,i){
    			var cirX = d.x;
    			var cirY = d.y;
    			return "translate("+cirX+","+cirY+")";
    		})
    		.call(d3.drag()
    			.on("start",started)
    			.on("drag",dragged)
    			.on("end",ended)
            )
            //繪製節點
            gs.append("circle")
                .attr("r",10)
                .attr("fill",function(d,i){
                    return colorScale(i);
                })
            //文字
            gs.append("text")
                .attr("x",-10)
                .attr("y",-20)
                .attr("dy",10)
                .text(function(d){
                    return d.name;
                })
             function ticked(){
    		links
    			.attr("x1",function(d){return d.source.x;})
    			.attr("y1",function(d){return d.source.y;})
    			.attr("x2",function(d){return d.target.x;})
    			.attr("y2",function(d){return d.target.y;});
    			
    		linksText
    			.attr("x",function(d){
    			return (d.source.x+d.target.x)/2;
    		})
    		.attr("y",function(d){
    			return (d.source.y+d.target.y)/2;
    		});
    			
    		gs
    			.attr("transform",function(d) { return "translate(" + d.x + "," + d.y + ")"; });

            }
            function started(d){
    		if(!d3.event.active){
    			forceSimulation.alphaTarget(0.8).restart();   //設置衰減係數,對節點位置移動過程的模擬,數值越高移動越快,數值範圍[0,1]
    		}
    		d.fx = d.x;
    		d.fy = d.y;
    	}
    	function dragged(d){
    		d.fx = d3.event.x;
    		d.fy = d3.event.y;
    	}
    	function ended(d){
    		if(!d3.event.active){
    			forceSimulation.alphaTarget(0);
    		}
    		d.fx = null;
    		d.fy = null;
        }
    }
}
</script>

在這裏插入圖片描述
15、樹狀圖
樹狀圖,可表示節點之間的包含與被包含關係。
現有數據如下:

{
"name":"中國",
"children":
[
    { 
      "name":"浙江" , 
      "children":
      [
            {"name":"杭州" },
            {"name":"寧波" },
            {"name":"溫州" },
            {"name":"紹興" }
      ] 
    },

    { 
        "name":"廣西" , 
        "children":
        [
            {
            "name":"桂林",
            "children":
            [
                {"name":"秀峯區"},
                {"name":"疊彩區"},
                {"name":"象山區"},
                {"name":"七星區"}
            ]
            },
            {"name":"南寧"},
            {"name":"柳州"},
            {"name":"防城港"}
        ] 
    },

    { 
        "name":"黑龍江",
        "children":
        [
            {"name":"哈爾濱"},
            {"name":"齊齊哈爾"},
            {"name":"牡丹江"},
            {"name":"大慶"}
        ] 
    },

    { 
        "name":"新疆" , 
        "children":
        [
            {"name":"烏魯木齊"},
            {"name":"克拉瑪依"},
            {"name":"吐魯番"},
            {"name":"哈密"}
        ]
    }
]
}

這段數據表示:“中國 – 省份名 – 城市名”的包含於被包含關係。
搭建HTTP服務器(解決Chrome無法讀取本地文件)
1.安裝Node
2.npm install http-server -g
3.目錄下執行 http-server -c-1
4.localhost:8080/xxxx.html
佈局(數據轉換)
定義一個集羣圖佈局:

var tree = d3.layout.tree() 
  	.size([width, height-200]) //設定尺寸
 	.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2); });//設定節點之間的間隔

接下來,轉換數據:

d3.json("city_tree.json", function(error, root) {     //可將數據定義在該頁面,不用.json模擬數據
  var nodes = tree.nodes(root);
  var links = tree.links(nodes);
  console.log(nodes);
  console.log(links);
}

繪製
D3 已經基本上爲我們準備好了繪製的函數:d3.svg.diagonal() 。這是一個對角線生成器,只需要輸入兩個頂點座標,即可生成一條貝塞爾曲線。
創建一個對角線生成器:

var diagonal = d3.svg.diagonal()     //d3.svg.diagonal()在v5中報錯
    .projection(function(d) { return [d.y, d.x]; });

projection() 是一個點變換器,默認是 [ d.x , d.y ],即保持原座標不變,如果寫成 [ d.y , d.x ] ,即是說對任意輸入的頂點,都交換 x 和 y 座標。
繪製連線時,使用方法如下:

var link = svg.selectAll(".link")
      .data(links)
      .enter()
      .append("path")
      .attr("class", "link")
      .attr("d", diagonal);   //使用對角線生成器
<template>
    <div class="tree">
       
    </div>
</template>
<script>
import * as d3 from 'd3'
export default {
    mounted(){
        var width=600;
        var height=500;
        var svg=d3.select(".tree")
                .append('svg')
                .attr('width',width)
                .attr('height',height);
        //定義邊界
    	var marge = {top:50, bottom:0, left:10, right:0};
    	var g = svg.append("g")
    		.attr("transform","translate("+marge.top+","+marge.left+")");
    	
    	var scale = svg.append("g")
            .attr("transform","translate("+marge.top+","+marge.left+")");
            
    	//數據
    	var dataset = {
    		name:"中國",
    		children:[
    			{
    				name:"浙江",
    				children:[
    					{name:"杭州" ,value:100},
    					{name:"寧波",value:100},
            			{name:"溫州",value:100},
            			{name:"紹興",value:100}
    				]
    			},
    			{
    				name:"廣西",
    				children:[
    					{
    						name:"桂林",
    						children:[
    							{name:"秀峯區",value:100},
                				{name:"疊彩區",value:100},
                				{name:"象山區",value:100},
               					{name:"七星區",value:100}
    						]
    					},
    					{name:"南寧",value:100},
            			{name:"柳州",value:100},
            			{name:"防城港",value:100}
    				]
    			},
    			{
    				name:"黑龍江",
    				children:[
    					{name:"哈爾濱",value:100},
            			{name:"齊齊哈爾",value:100},
            			{name:"牡丹江",value:100},
            			{name:"大慶",value:100}
    				]
    			},
    			{
    				name:"新疆" , 
        			children:
        			[
			            {name:"烏魯木齊"},
			            {name:"克拉瑪依"},
			            {name:"吐魯番"},
			            {name:"哈密"}
        			]
    			}
    		]
        }; 
        var hierarchyData = d3.hierarchy(dataset)
    		.sum(function(d){
    			return d.value;
    		});       
        var tree=d3.tree()
                .size([width-200,height-200])    //設置尺寸
                .separation(function(a,b){
                    return (a.parent == b.parent ?1:2);
                })
        var treeData = tree(hierarchyData);
        var nodes=treeData.descendants();
        var links=treeData.links();
        console.log(nodes);
        console.log(links);

        var Bézier_curve_generator = d3.linkHorizontal()
    		.x(function(d) { return d.y; })
            .y(function(d) { return d.x; });
            
        //繪製邊
    	g.append("g")
    		.selectAll("path")
    		.data(links)
    		.enter()
    		.append("path")
    		.attr("d",function(d){
    			var start = {x:d.source.x,y:d.source.y};
    			var end = {x:d.target.x,y:d.target.y};
    			return Bézier_curve_generator({source:start,target:end});
    		})
    		.attr("fill","none")
    		.attr("stroke","yellow")
            .attr("stroke-width",1);
            
        var gs = g.append("g")
    		.selectAll("g")
    		.data(nodes)
    		.enter()
    		.append("g")
    		.attr("transform",function(d){
    			var cx = d.x;
    			var cy= d.y;
    			return "translate("+cy+","+cx+")";
            });
            
        //繪製節點
    	gs.append("circle")
    		.attr("r",6)
    		.attr("fill","white")
    		.attr("stroke","blue")
    		.attr("stroke-width",1);
    		
    	//文字
    	gs.append("text")
    		.attr("x",function(d){
    			return d.children?-40:8;
    		})
    		.attr("y",-5)
    		.attr("dy",10)
    		.text(function(d){
    			return d.data.name;
    		})
    }
}
</script>

在這裏插入圖片描述
16、地圖可視化
在數據可視化中,地圖是很重要的一部分。很多情況會與地圖有關聯,如中國各省的人口多少,GDP多少等,都可以和地圖聯繫在一起。
D3地圖繪製
製作地圖需要 JSON 文件,將 JSON 的格式應用於地理上的文件,叫做 GeoJSON 文件。
*投影函數

 var projection = d3.geo.mercator()    //投影函數
        .center([107, 31])           //設定地圖的中心位置--經度和緯度
        .scale(850)         //設定放大的比例
        .translate([width/2, height/2]);          //設定平移

由於 GeoJSON 文件中的地圖數據,都是經度和緯度的信息。它們都是三維的,而要在網頁上顯示的是二維的,所以要設定一個投影函數來轉換經度緯度。如上所示,使用 d3.geo.mercator() 的投影方式。
*地理路徑生成器
爲了根據地圖的地理數據生成 SVG 中 path 元素的路徑值,需要用到 d3.geo.path(),稱爲地理路徑生成器。

var path = d3.geo.path()
    .projection(projection);

projection() 是設定生成器的投影函數,把上面定義的投影傳入即可。

*加載文件並繪製地圖

d3.json(“world.json”, function(error, root) {

if (error) 
    return console.error(error);
console.log(root.features);

svg.selectAll("path")
    .data( root.features )
    .enter()
    .append("path")
    .attr("stroke","#000")
    .attr("stroke-width",1)
    .attr("fill", function(d,i){
        return color(i);
    })
    .attr("d", path )   //使用地理路徑生成器
    .on("mouseover",function(d,i){
                d3.select(this)
                   .attr("fill","yellow");
            })
            .on("mouseout",function(d,i){
                d3.select(this)
                   .attr("fill",color(i));
            });

});

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章