繼上一篇完成熱門標籤趨勢圖後,我們可以進一步進行資料視覺化的一項重要功能:互動式介面,透過互動式的介面可以讓使用者更進一步的探索資料,如何設計互動式的功能及效果也是十分重要的。
為了更近一步看出趨勢的變化,我們可以設計將每個時間軸相同的標籤相連,以此來觀察時間趨勢的變化,另一個重要的互動式介面則是顯示功能,如何有效地告訴使用者詳細的資訊可以更大程度的瞭解資料。
[
{
"tagId": 3,
"tagName": "情人",
"lineData": [
{
"time": "2017/03/26",
"rank": 3
},
{
"time": "2017/04/02",
"rank": 3
},
{
"time": "2017/04/09",
"rank": 3
},
...
]
},
...
]
將資料組成以上結構,lineData 表示的是每個標所有的時間資料
let lineFunction = d3.line()
.x(function(d) {
return x(new Date(d.time));
})
.y(function(d) {
return y(d.rank);
})
function drawLine( tagLineArray ){
// The line SVG Path we draw
// 每一條線代表一個標籤的 lifespan 出現點
for( let i=0; i< tagLineArray.length; i++){
bubbles.data([tagLineArray[i].lineData])
.append("path")
.attr("class", "line"+ "tag"+tagLineArray[i].tagId)
.attr("stroke","none")
.style("fill", "none")
.style("stroke-width", "2px")
.attr("d", lineFunction);
}
}
lineFunction - 將每一個 path 的位置 mapping 到實際的資料值域上
如此一來就可以將相同的標籤圓圈連線,但此時會遇到一個小問題,由於 SVG 疊加在畫布上的時候會有先後順序的問題,因此我們需要將 SVG 的疊加順序改成已選取的在最上層,如此才不會有已選取的標籤卻被其他的包千遮住的問題。
// changing back and front
// https://github.com/wbkd/d3-extended
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
this.parentNode.appendChild(this);
});
};
d3.selection.prototype.moveToBack = function() {
return this.each(function() {
var firstChild = this.parentNode.firstChild;
if (firstChild) {
this.parentNode.insertBefore(this, firstChild);
}
});
};
為了讓使用者知道每一個圓圈的標籤內容,可在滑鼠移到圓圈時顯示一個帶有標籤資訊的小視窗,此視窗則稱為 tooltip。
var tool_tip = d3.tip() //tooltip to show the box of the message
.attr("class", "tips")
.offset([-10, 0]);
function biingDataAndDrawingBubble( data, based_term, svgElement ){
var color_based_array = []; //mean or total_pv
var article_counts_array = [];
//取得總pv數的序數陣列為的是取得十分位數的切點
//依據文章的排序將之作為大小正規化的切點
data.sort(function(a,b) { return +a.article_counts - +b.article_counts; });
for( var i=0; i<data.length; i++ ){
data[i].countsId = i;
article_counts_array.push(data[i].article_counts);
if(based_term == "pv")
color_based_array.push(data[i].total_pv);
else if(based_term == "mean")
color_based_array.push(data[i].mean);
}
//大小的正規化方法
var sizeScale = d3.scaleQuantile()
.domain([0, data.length/2, data.length])
.range([5, 10, 20]);
//顏色的十分位數正歸化方法
var colorQuantile = d3.scaleQuantile()
.domain(color_based_array)
.range([0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);
//將十個數值對應到顏色的數值裡
var colorScaleByQuantile = d3.scaleSequential()
.domain([0, 100]);
(based_term == "pv") ? colorScaleByQuantile.interpolator(d3.interpolatePlasma) : colorScaleByQuantile.interpolator(d3.interpolateViridis);
svgElement.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class", function(d){
return "tag" + d.tagID;
})
.attr("cx", function(d){
var t = x(new Date(d.weekStart));
return t-4;
})
.attr("cy", function(d){
var yPosition;
(based_term == "pv") ? yPosition = y(d.total_pv_rank) : yPosition = y(d.meanRank);
return yPosition;
})
.attr("r", function(d){
var size = sizeScale(d.countsId);
return size;
})
.attr("fill-opacity","0.6")
.attr("stroke","black")
.attr("stroke-width",0)
.attr("fill", function(d){
var quantileValue;
(based_term == "pv") ? quantileValue = colorQuantile(d.total_pv) : quantileValue = colorQuantile(d.mean);
return colorScaleByQuantile(quantileValue);
})
.on('mouseover', function(d){
tool_tip.show(d);
var s = ".tag" + d.tagID;
var l = ".linetag" + d.tagID;
d3.selectAll(s).moveToFront();
d3.selectAll(s).attr("fill-opacity","1.0")
d3.selectAll(s).attr("stroke-width",2);
d3.selectAll(l).attr("stroke", "brown");
})
.on('mouseout', function(d){
tool_tip.hide(d);
var s = ".tag" + d.tagID;
var l = ".linetag" + d.tagID;
var quantileValue;
(based_term == "pv") ? quantileValue = colorQuantile(d.total_pv) : quantileValue = colorQuantile(d.mean);
d3.selectAll(s).moveToBack();
d3.selectAll(l).attr("stroke", "none");
d3.selectAll(s).attr("stroke-width",0);
d3.selectAll(s).attr("fill-opacity","0.6")
});
//==================set tooltip==========================
(based_term == "pv") ? tool_tip.html(function(d) {
return "Tag: "+d.tagName+"</br>文章篇數: "+d.article_counts+"</br>總pv: "+addingComma(d.total_pv)+"</br>";
}) : tool_tip.html(function(d) {
return "Tag: "+d.tagName+"</br>文章篇數: "+d.article_counts+"</br>平均pv: "+addingComma(Math.round(d.mean*100)/100)+"</br>";
})
}
mouseover - 當滑鼠移到圓圈上時顯示 tooltip 並顯示線條以及將所有元素移到最上面
mouseout - 讓線條消失以及將元素恢復原來的位置