增強 D3.js 的視覺化功能
互動篇
前言
在這篇會討論的內容有增加篩選資料功能、增加動畫效果,使你的資料看起來和緩自然,最後,我們增加一些 tooltips,讓使用者 hover 到某個地方時,會顯示該資料的內容。
Filter
在 D3.js 基礎繪圖中,有提到我為 scatter plot 製作了一個特殊的功能:只顯示 min/max 的點,我今天打算為他來製作新功能:使用者可以透過下拉選單選擇顯示全部點的數字、只顯示 min/max、以及不顯示。
為了完成這個目的,我們要先做到兩件事:調整 showMinMax 函式(這個命名不是很好)
還有在下拉選單的挑選時,讓 js 知道前端 select 元素更改的事件。
雖然我們有 jQuery 可以使用,但為了這次的練習,使用 d3 的 select 函式來做處理:
<select id="kpi">
<option value="none">None</option>
<option value="minmax">Min/Max</option>
<option value="all">All</option>
</select>
其中,select(“#kpi”).node().value 是回傳當前選擇的元素 value,意思是如果我在前端選 None,則 s 會得到 none。
執行結果如下:
上述是第一種常用的 filter。
我們還有另外一種 filter 方式,例如我為前面練習的天氣資料做擷取,我只只想看某個特定區段的時間,但是礙於篇幅,會於另外一篇中做討論。
Transitions
這篇會討論一些動畫以及 Transition,主要目的是讓使用者看起來更有互動的感覺。
我們一樣以更改 scatter plot 為基礎進行練習。
首先我們在產生圓點的地方加入 transition(),這樣可以讓圖形呈現的方式變得較為動態,接著我們可以利用一個叫 duration() 的函式,接在 transition() 後面,來延遲圖形呈現。
我們也可以選擇呈現的方式,我們可以使用一個函式叫 ease(),傳入一參數 d3.easeBounce 來讓他呈現圖形時會有躍動的視覺效果。
練習的程式碼如下:
Tool tips
我們很常看到 tool tips,當我們的鼠標移到某個地方的時候,他會跳出一個小格子,裡面會有相對應的數字或提示。
在這邊我們要探討兩種加入 tool tips 方式,第一種是利用 HTML5 的 title attritube ,直接對圖形加 title,另一種則是透過 mousehover 事件來顯示 tool tips 。
首先,我們看第一種的做法,我們直接在前面製作的 bar chart 圖形後面直接加入以下兩行程式碼:
.append("title")
.text(function(d) { return d; })
這樣的意思是,我在每個 rect 裡面再 append 一個 title 元素,然後該 title 內容就是數字內容。執行結果如下:
第二種比較複雜,我們預計直接讓這些 bar chart 在滑鼠指標指到的時候,在該長條的頂部顯示數字。
我們首先在長條圖中加入數字:
接著,我要撰寫當這個 rect 被鼠標 hover 時,才顯示數字,所以我要把產生 text 的 function 稍作修改。
我們利用 on(“mouseover”) 時顯示數字,on(“mouseout”) 達到讓數字消失之功效,所以我先在加這幾段 svg.selectAll(“rect”) 這段宣告長條的定義後面補上這樣的內容:
.on("mouseover", function(d){
svg.append("text")
.text(d)
})
.on("mouseout", function(d){
d3.select("").remove();
})
我們在 mouseover 事件時,直接在 svg 底下放入 text,並且當 mouseout 事件時,刪除這個文字,不過問題來了,因為直接在 svg 底下放text,文字不會跑到 rect 裡面,所以我們要對文字的 x、y 軸以及顯示的顏色做設定:
svg.append("text")
.text(d)
.attr("x", parseFloat(d3.select(this).attr("x")) + parseFloat(d3.select(this).attr("width")/2) - 11)
.attr("y", parseFloat(d3.select(this).attr("y")) + 20)
.attr("fill", "#fff")
大家看到這段程式碼,應該會疑惑兩件事:parseFloat(d3.select(this).attr(“x”)) 這是甚麼?然後怎麼有魔術數字(-11 +20 之類特定的數字),魔術數字主要是調整文字視覺上的排版,這並不是很好的示範,但因為時間有限,所以我就沒用 scale 等方法讓他根據螢幕大小來縮放。如果你要做到 RWD,建議使用 scale 來處理等比例縮放問題。在來,我們討論 d3.select(this).attr(“x”) 這幾個出現在上述程式碼, d3.select(this) 表示抓取當前我 mouseover 的元素資料,我要抓取這個長條的起始 x 座標,並且計算出長條的寬度,使我的文字可以顯示在長條的中間,那 parseFloat(d3.select(this).attr(“y”)) 一樣是要抓取長條的高度,好讓我的文字顯示在上面。
所以,根據上面的邏輯,我們來執行這段程式碼:
文字都有出來,排版也還可以,但是文字並沒有在 mouseout 事件發生時消失。
所以,我們要做最後一件事:指定被 mouseover 的長條有個獨立的 ID,並且在 mouseout 時, d3 去移除他原本選取的 id,對於 d3 的 selector 運作模式,我會在附錄中講解。
所以,我對產生的 text 指定其 id 元素為 tooltip,並且在 mouseout 時,透過 d3 的 selector 去移除 tooltip 。
最後執行結果如下:
結論
在這個文章中,我們探討了幾個常見的互動方式,第一種是透過 filter 來撈取範圍資料,加強整個圖形重點,第二個是研究 Transition,讓產生圖表的方式有看起來更有「產生圖形的動作」,最後我們建立了簡單的 tool tip,讓讀者可以看到數字或事該筆資料的某些訊息提示 。
補充
- ease() 支援的幾個傳入值
d3.easeElastic
d3.easeBounce
d3.easeLinear
d3.easeSin
d3.easeQuad
d3.easeCubic
d3.easePoly
d3.easeCircle
d3.easeExp
d3.easeBack
2. 關於 D3.select(selector) 的運作模式
我們在前面有看到
d3.select("#tooltip").remove();
他到底怎麼找到正確的 text#tooltip 並刪除呢?我們可以從 d3 的官方文件看到以下 d3 selector 的運作模式
Selects the first element that matches the specified selector string. If no elements match the selector, returns an empty selection. If multiple elements match the selector, only the first matching element (in document order) will be selected.
他的意思是,如果我的選擇器找到一個或多個元素,我永遠回傳第一個元素回去,除非這個選擇器沒選到東西,它才會回傳空的選擇,所以,我們的程式在這樣的運作邏輯下,我們在下一個 mouseover 之前會先 mouseout,此時的 text#tooltip 只有唯一當前 mouseover 的那一個,程式才會刪除對的 text#tooltip 。