【D3.js 入門系列二】理解 Update && Enter && Exit、製作互動式動態圖表

混沌傳奇發表於2018-03-13

上一章:《【D3.js 入門系列一】從零開始繪製一個柱形圖》

宣告:本教程針對D3.js v3版本進行講解。

【Lesson1】理解 Update、Enter、Exit

Update、Enter、Exit 是 D3 中三個非常重要的概念,它處理的是當選擇集和資料的數量關係不確定的情況。

1. 什麼是 Update、Enter、Exit

前面章裡,反覆出現了形如以下的程式碼。

svg.selectAll("rect")   //選擇svg內所有的矩形
    .data(dataset)      //繫結陣列
    .enter()            //指定選擇集的enter部分
    .append("rect")     //新增足夠數量的矩形元素
複製程式碼

前面提到,這段程式碼使用的情況是當以下情況出現的時候:

有資料,而沒有足夠圖形元素的時候,使用此方法可以新增足夠的元素。

當時並沒有深究這段程式碼是什麼意思,本章將對此進行講解。但是,由於此問題相對複雜,本章只進行最初步的介紹。

假設,在 body 中有三個 p 元素,有一陣列 [3, 6, 9],則可以將陣列中的每一項分別與一個 p 元素繫結在一起。但是,有一個問題:當陣列的長度與元素數量不一致(陣列長度 > 元素數量 or 陣列長度 < 元素數量)時呢?這時候就需要理解 Update、Enter、Exit 的概念。

如果陣列為 [3, 6, 9, 12, 15],將此陣列繫結到三個 p 元素的選擇集上。可以想象,會有兩個資料沒有元素與之對應,這時候 D3 會建立兩個空的元素與資料對應,這一部分就稱為 Enter。而有元素與資料對應的部分稱為 Update。如果陣列為 [3],則會有兩個元素沒有資料繫結,那麼沒有資料繫結的部分被稱為 Exit。示意圖如下所示。

【D3.js 入門系列二】理解 Update && Enter && Exit、製作互動式動態圖表

看到這,我想大家能體會到為什麼本節最開始處的程式碼能夠給 SVG 內新增足夠數量的元素了吧。它的意思其實是:

此時 SVG 裡沒有 rect 元素,即元素數量為 0。有一陣列 dataset,將陣列元素數量為 0 的選擇集繫結後,選擇其 Enter 部分(請仔細看上圖),然後新增(append)元素,也就是新增足夠的元素,使得每一個資料都有元素與之對應。

2. Update 和 Enter 的使用

當對應的元素不足時 ( 繫結資料數量 > 對應元素 ),需要新增元素(append)。

現在 body 中有三個 p 元素,要繫結一個長度大於 3 的陣列到 p 的選擇集上,然後分別處理 update 和 enter 兩部分。

var dataset = [ 3 , 6 , 9 , 12 , 15 ];
 
//選擇body中的p元素
var p = d3.select("body").selectAll("p");
 
//獲取update部分
var update = p.data(dataset);
 
//獲取enter部分
var enter = update.enter();
 
//update部分的處理:更新屬性值
update.text(function(d){
    return "update " + d;
});
 
//enter部分的處理:新增元素後賦予屬性值
enter.append("p")
    .text(function(d){
        return "enter " + d;
    });
複製程式碼

結果如下圖,update 部分和 enter 部分被繫結的資料很清晰地表示了出來。

【D3.js 入門系列二】理解 Update && Enter && Exit、製作互動式動態圖表

請大家記住:

  • update 部分的處理辦法一般是:更新屬性值
  • enter 部分的處理辦法一般是:新增元素後,賦予屬性值

3. Update 和 Exit 的使用

當對應的元素過多時 ( 繫結資料數量 < 對應元素 ),需要刪掉多餘的元素。

現在 body 中有三個 p 元素,要繫結一個長度小於 3 的陣列到 p 的選擇集上,然後分別處理 update 和 exit 兩部分。

var dataset = [ 3 ];
 
//選擇body中的p元素
var p = d3.select("body").selectAll("p");
 
//獲取update部分
var update = p.data(dataset);
 
//獲取exit部分
var exit = update.exit();
 
//update部分的處理:更新屬性值
update.text(function(d){
    return "update " + d;
});
 
//exit部分的處理:修改p元素的屬性
exit.text(function(d){
        return "exit";
    });
 
//exit部分的處理通常是刪除元素
// exit.remove();
複製程式碼

結果如下,請大家區分好 update 部分和 exit 部分。這裡為了表明哪一部分是 exit,並沒有刪除掉多餘的元素,但實際上 exit 部分的絕大部分操作是刪除。

【D3.js 入門系列二】理解 Update && Enter && Exit、製作互動式動態圖表

請大家記住:

  • exit 部分的處理辦法一般是:刪除元素(remove)

【Lesson2】讓圖表動起來

D3 支援製作動態的圖表。有時候,圖表的變化需要緩慢的發生,以便於讓使用者看清楚變化的過程,也能給使用者不小的友好感。

1. 什麼是動態效果

前面一章製作的圖表是一蹴而就地出現,然後繪製完成後不再發生變化的,這是靜態的圖表。

動態的圖表,是指圖表在某一時間段會發生某種變化,可能是形狀、顏色、位置等,而且使用者是可以看到變化的過程的。

例如,有一個圓,圓心為 (100, 100)。現在我們希望圓的 x 座標從 100 移到 300,並且移動過程在 2 秒的時間內發生。

這種時候就需要用到動態效果,在 D3 裡我們稱之為過渡(transition)

2. 實現動態的方法

D3 提供了 4 個方法用於實現圖形的過渡:從狀態 A 變為狀態 B

transition()

啟動過渡效果。

其前後是圖形變化前後的狀態(形狀、位置、顏色等等),例如:

.attr("fill","red")         //初始顏色為紅色
.transition()               //啟動過渡
.attr("fill","steelblue")   //終止顏色為鐵藍色
複製程式碼

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

duration()

指定過渡的持續時間,單位為毫秒。

如 duration(2000) ,指持續 2000 毫秒,即 2 秒。

ease()

指定過渡的方式,常用的有:

  • linear:普通的線性變化
  • circle:慢慢地到達變換的最終狀態
  • elastic:帶有彈跳的到達最終狀態
  • bounce:在最終狀態處彈跳幾次

呼叫時,格式形如: ease(“bounce”)。

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 毫秒。

3. 實現簡單的動態效果

下面將在 SVG 畫布裡新增三個圓,圓出現之後,立即啟動過渡效果。

第一個圓,要求移動 x 座標。

var circle1 = svg.append("circle")
        .attr("cx", 100)
        .attr("cy", 100)
        .attr("r", 45)
        .style("fill","green");
 
//在1秒(1000毫秒)內將圓心座標由100變為300
circle1.transition()
    .duration(1000)
    .attr("cx", 300);
複製程式碼

第二個圓,要求既移動 x 座標,又改變顏色。

var circle2 = svg.append("circle")... //與第一個圓一樣,省略部分程式碼
 
//在1.5秒(1500毫秒)內將圓心座標由100變為300,
//將顏色從綠色變為紅色
circle2.transition()
    .duration(1500)
    .attr("cx", 300)
    .style("fill","red");
複製程式碼

第三個圓,要求既移動 x 座標,又改變顏色,還改變半徑。

var circle3 = svg.append("circle")... //與第一個圓一樣,省略部分程式碼
 
//在2秒(2000毫秒)內將圓心座標由100變為300
//將顏色從綠色變為紅色
//將半徑從45變成25
//過渡方式採用bounce(在終點處彈跳幾次)
circle3.transition()
    .duration(2000)
    .ease("bounce")
    .attr("cx", 300)
    .style("fill","red")
    .attr("r", 25);
複製程式碼

4. 給柱形圖加上動態效果

在上一章完整柱形圖的基礎上稍作修改,即可做成一個帶動態效果的、有意思的柱形圖。

在新增文字元素和矩形元素的時候,啟動過渡效果,讓各柱形和文字緩慢升至目標高度,並且在目標處跳動幾次。

對於文字元素,程式碼如下:

.attr("y",function(d){
    var min = yScale.domain()[0];
    return yScale(min);
})
.transition()
.delay(function(d,i){
    return i * 200;
})
.duration(2000)
.ease("bounce")
.attr("y",function(d){
    return yScale(d);
});
複製程式碼

文字元素的過渡前後,發生變化的是 y 座標。其起始狀態是在 y 軸等於 0 的位置(但要注意,不能在起始狀態直接返回 0,要應用比例尺計算畫布中的位置)。終止狀態是目標值。

對於矩形元素,思想與文字元素一樣,只是在計算起始狀態時要稍微複雜一些,請讀者自行研讀頁面底部的示例程式碼地址中的程式碼。


【Lesson3】互動式操作

與圖表的互動,指在圖形元素上設定一個或多個監聽器,當事件發生時,做出相應的反應。

【D3.js 入門系列二】理解 Update && Enter && Exit、製作互動式動態圖表

1. 什麼是互動

互動,指的是使用者輸入了某種指令,程式接受到指令之後必須做出某種響應。對視覺化圖表來說,互動能使圖表更加生動,能表現更多內容。例如,拖動圖表中某些圖形、滑鼠滑到圖形上出現提示框、用觸屏放大或縮小圖形等等。

使用者用於互動的工具一般有三種:滑鼠、鍵盤、觸屏。

2. 如何新增互動

對某一元素新增互動操作十分簡單,程式碼如下:

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:當使用者釋放鍵時觸發,不區分字母的大小寫。

觸屏常用的事件有三個:

  • touchstart:當觸控點被放在觸控式螢幕上時。
  • touchmove:當觸控點在觸控式螢幕上移動時。
  • touchend:當觸控點從觸控式螢幕上拿開時。

當某個事件被監聽到時,D3 會把當前的事件存到 d3.event 物件,裡面儲存了當前事件的各種引數,請大家好好參詳。如果需要監聽到事件後立刻輸出該事件,可以新增一行程式碼:

circle.on("click", function(){
    console.log(d3.event);
});
複製程式碼

3. 帶有互動的柱形圖

將【D3.js 入門系列一】的部分程式碼修改成如下程式碼。

var rects = svg.selectAll(".MyRect")
        .data(dataset)
        .enter()
        .append("rect")
        .attr("class","MyRect")   //把類裡的 fill 屬性清空
        .attr("transform","translate(" + padding.left + "," + padding.top + ")")
        .attr("x", function(d,i){
            return xScale(i) + rectPadding/2;
        } )
        .attr("y",function(d){
            return yScale(d);
        })
        .attr("width", xScale.rangeBand() - rectPadding )
        .attr("height", function(d){
            return height - padding.top - padding.bottom - yScale(d);
        })
        .attr("fill","steelblue")       //填充顏色不要寫在CSS裡
        .on("mouseover",function(d,i){
            d3.select(this)
                .attr("fill","yellow");
        })
        .on("mouseout",function(d,i){
            d3.select(this)
                .transition()
                .duration(500)
                .attr("fill","steelblue");
        });
複製程式碼

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

mouseover 監聽器函式的內容為:將當前元素變為黃色

mouseout 監聽器函式的內容為:緩慢地將元素變為原來的顏色(藍色)

程式碼示例地址:github.com/legend-li/D…

未完待續~

參考資料:www.ourd3js.com/
D3.js(v3)中文api:github.com/d3/d3/wiki/…

相關文章