利用HTML5 Canvas創(chuàng)建交互式Bubble Chart

Josh Marinacci也在其博客中詳細地介紹了“如何利用HTML5 Canvas創(chuàng)建可以在移動桌面上運行的交互式圖表以及如何利用真實數(shù)據(jù)填充圖表。”下面讓我們跟隨作者學習如何創(chuàng)建我們自己的Bubble Chart。
準備工作:
需要知識:媒介HTML和JavaScript
需求:文本編輯器和現(xiàn)代瀏覽器
關于Bubble Chart
Bubble Chart就和它聽起來一樣是由氣泡組成的圖表,氣泡實際上就是一些圓。但是Bubble Chart卻能可視化五維數(shù)據(jù),這也是其強大之處。每個氣泡代表由 X和Y坐標定位的數(shù)據(jù)點,這和任何其他的線狀圖和散點圖一樣。可是每個氣泡的大小和顏色屬性可以表示另外兩維的數(shù)據(jù)。如果我們動態(tài)顯示圖表,那么我們可以增加時間作為第五維數(shù)據(jù)。
盡管Bubble Chart的五個屬性都可以代表任何類型的數(shù)據(jù),但是實踐中我們通常用X和Y坐標代表數(shù)字型數(shù)據(jù),而氣泡顏色這個屬性用于區(qū)分兩個不同的數(shù)據(jù)集。例如利用X和Y分表代表兒童的“死亡數(shù)”和“受教育比例”,而氣泡顏色代表不同的國家。氣泡大小一般用來代表某種事物的數(shù)量如某個國家的人口。當動態(tài)顯示圖標時,第五維的坐標用來代表時間如表示從1960年到現(xiàn)在。仔細地創(chuàng)造性地使用這些氣泡屬性是創(chuàng)造出一個與眾不同的資訊圖標的關鍵。
利用模擬數(shù)據(jù)創(chuàng)建簡單的圖表
數(shù)據(jù)本身應該代表按照一定標準分組隨著時間變化的數(shù)據(jù)點。假設數(shù)據(jù)由5格國家,20個時間點(從1980到2000年)的數(shù)據(jù)組成。每個數(shù)據(jù)點由x、y和大小三個屬性組成。初始化這些模擬數(shù)據(jù)的代碼如下:
var data = [];
for(var t=0; t<20; t++) {
var cdata = [];
data[t] = cdata;
for(var country=0; country<5; country++) {
cdata.push({
x:50+Math.random()*500,
y:50+Math.random()*300 ,
size: 3+Math.random()*20,
country: country});
}
}
接下來在屏幕上用氣泡描繪出這些數(shù)據(jù),代碼如下:
var canvas = document.getElementById(‘canvas’);
var ctx = canvas.getContext(’2d’);
var colors = ["red","green","blue","yellow","orange"];
var time = 0;
function draw() {
//bg and border
ctx.fillStyle = ”white”;
ctx.fillRect(0,0, canvas.width,canvas.height);
ctx.strokeStyle = ”black”;
ctx.strokeRect(0,0,canvas.width,canvas.height);
//time indicator
ctx.fillStyle = ”black”;
ctx.fillText(“time “ + time, 10,20);
//draw the data for the current time slice
data[time].forEach(function(d) {
ctx.save();
ctx.fillStyle = colors[d.country%colors.length];
ctx.globalAlpha = 0.5;
ctx.beginPath();
ctx.arc(d.x,d.y,d.size,0,Math.PI*2);
ctx.fill();
ctx.restore();
});
}
上述代碼用白色填充背景,利用Canvas邊框作為背景的邊界。然后描繪當前時間指示器,***描繪數(shù)據(jù)本身。每個數(shù)據(jù)點都根據(jù)當前點的x、y和大小描繪成相應的圓。每個國家的顏色從定義的顏色列表中選取。
基本表如下:

為了增加表的時間屬性,我們只需要用不同的時間變量重復調用描繪函數(shù)重復描繪即可。代碼如下:
$(“#play”).click(function() {
var animdraw = function() {
draw();
time++;
if(time < data.length) {
setTimeout(animdraw,100);
} else {
time = 0;
}
}
animdraw();
});
獲取“世界數(shù)據(jù)銀行”的數(shù)據(jù)
至此已經創(chuàng)建了一個基本表。下面讓我們利用一些真實的數(shù)據(jù)填充這個表讓它更有意思。我們從“世界數(shù)據(jù)銀行”選擇需要的數(shù)據(jù),它是一個龐大的組織,具有從UN和其他公開的數(shù)據(jù)源采集到的龐大的數(shù)據(jù)集。除了數(shù)據(jù)承載基地,它還有一個定制的報表生成器,它可以讓你創(chuàng)建數(shù)據(jù)切片以支持多種格式的數(shù)據(jù)下載。
本文選擇了“世界發(fā)展指標和全球發(fā)展金融”數(shù)據(jù)庫。選擇“東亞及太平洋地區(qū)”,“歐洲和中亞”,“拉丁美洲和加勒比地區(qū)”一些國家或地區(qū)作為集合,利用氣泡的顏色屬性表示不同的集合。選擇“死亡率”、“人口密度”、“總人口”作為研究點,分別用x、y坐標以及氣泡的大小屬性表示。***選擇了時間變量。簡單的點擊“全選”按鈕即可選擇從1960年到現(xiàn)在以年為單位的數(shù)據(jù)變量。
現(xiàn)在我們已經選定了一份報告。點擊“導出”按鈕下載CSV文件格式的數(shù)據(jù)。我們可以利用Excel打開CSV文件查看從“世界數(shù)據(jù)銀行”所得到的這份報告的內容。如果你利用Excel打開下載的CSV文件,將看到每個國家或者地區(qū)都有三行組成,我們選擇的每個變量都有一行。另外你可能看到早年的一些數(shù)據(jù)值并不存在,這意味著那幾年并沒有收集這些國家的數(shù)據(jù)。
解析CSV數(shù)據(jù)
如果你用Excel打開報告,它看起來只是一堆行。我們需要把數(shù)據(jù)解析成我們能用的形式。為了解析數(shù)據(jù)文件,我們需要CSV解析程序。Josh Marinacci從Ben Nadel下載了一個解析程序,很好用。為了下載CSV文件并解析它Josh Marinacci使用AJAX調用jQuery。
$(“#load”).click(function() {
$.ajax({
url: ”data.csv”,
context: document.body,
success: function(c) {
var csvdata = CSVToArray(c);
console.log(“got to here “ + csvdata[1]);
}
});
}
CSV文件被解析成由許多行組成的文件,這些行可以繼續(xù)進行子劃分被劃分成列。我們可以通過先列循環(huán)再行循環(huán)的方式處理CSV文件。
data = [];
//start at 9 to skip non-year columns and first few years
for(var t=9; t
var cdata = [];
//loop through rows by threes
for(var i=1; i
var row_mort = csvdata[i];
var row_dens = csvdata[i+1];
var row_total = csvdata[i+2];
var country = (i-1)/3;
cdata.push({
x:row_dens[t] ,
y:row_mort[t],
size: row_total[t],
country: country,
});
}
data[t-9] = cdata;
}
注意上述代碼是從第9列開始處理數(shù)據(jù)的,不僅跳過了數(shù)據(jù)不完整的的前幾年的數(shù)據(jù)點也跳過了一些元數(shù)據(jù)(如國家名字等等)。處理行的時候每次循環(huán)是處理三行數(shù)據(jù)目的是通過一次循環(huán)可以處理一個國家或地區(qū)的數(shù)據(jù)。
如果僅僅按照這種方式處理數(shù)據(jù),我們將什么都看不到。整個Canvas都被單一的顏色填充,這不是我們想要的結果。這是因為數(shù)據(jù)并沒有按比例進行適當?shù)臄U展。例如在1960年東亞和太平洋地區(qū)的人口是10億。為了描繪圖表需要按比例縮小這個值。為了適用于這些未修改的數(shù)據(jù)在繪制圖表的時Josh Marinacci使用了可擴展的功能。只需要采用如下方式改變弧度即可:
ctx.arc(
d.x*6,
canvas.height-d.y*2.5,
d.size/(1000*1000*11),
0,Math.PI*2
);
上述代碼修改了x、y和氣泡大小的值適應Canvas。氣泡大小必須除以1100萬才能得到合理的氣泡半徑。Y值由Canvas高度減去y乘以2.5得到。Canvas的標準坐標系要從左上方開始以便被Canvas高度減去之后可以翻轉y坐標。
修改數(shù)據(jù)變量的值是可以任意選取的,選擇某個特定的值是為了讓表格看起來比較美觀。具體選擇什么值依賴于正在描繪的數(shù)據(jù)。可以寫一個程序分析數(shù)據(jù)尋找合理的參數(shù)值,比如通過計算每一個變量的***值或者最小值。
表格看起來如下:

增加交互性
賦予表格時間這一點很好,增加表格的交互性實際上就是讓圖表閱讀者通過點擊不同的氣泡獲取更多的信息。由于Canvas完全使用像素而不是我們需要的圖形,這種圖形使得通過程序我們可以計算哪一個圖形被點擊了。幸運地是Canvas中的氣泡都是***的圓,這將使事情容易很多。我們只需要計算被點擊點與氣泡圓心的距離即可,如果距離小于半徑就認為該氣泡被點擊。代碼如下:
$(“#canvas”).mousedown(function(e) {
displayInfo = false;
data[time].forEach(function(d) {
var x = d.x*6;
var y = canvas.height-d.y*2.5;
var radius = d.size/(1000*1000*11);
var dis = dist(e.offsetX,e.offsetY,x,y);
if(dis < radius) {
displayInfo = true;
displayCountry = d.country;
}
});
draw();
});
如果讀者已經點擊了某個圖形,顯示信息的布爾值將變?yōu)?ldquo;true”,保存當前的國家并觸發(fā)重繪操作,同時在右上方通過代碼嵌入一個小的信息板。
注意在重繪代碼中填充的數(shù)據(jù)是來自于當前時間片而不是用戶點擊表格時的時間。這就意味著當表格被賦予時間屬性之后顯示的內容將更新以便用戶可以看到隨著時間變化的數(shù)據(jù)。
if(displayInfo) {
ctx.save();
ctx.translate(canvas.width-305,5);
ctx.fillStyle = ”rgba(200,200,200,0.7)”;
ctx.fillRect(0,0,300,100);
ctx.strokeStyle = ”black”;
ctx.lineWidth = 2;
ctx.strokeRect(0,0,300,100);
ctx.fillStyle = ”black”;
var displayPoint = data[time][displayCountry];
ctx.fillText(“Region: “ + regions[displayPoint.country],5,20);
ctx.fillText(“Population Density: “ + displayPoint.x,5,20+20*1);
ctx.fillText(“Mortality rate: “ + displayPoint.y,5,20+20*2);
ctx.fillText(“Population: “ + displayPoint.size,5,20+20*3);
ctx.restore();
}
改變圖表的視覺效果
一般而言圖報表不應該有這么多的可視元素否則將減損數(shù)據(jù)的潛在表現(xiàn)性,但是選擇一個***的顏色和模式將產生巨大的差別。為了讓這個表看起來更好,Josh Marinacci給每個氣泡增加了一層白色覆蓋物使圓更加***。同時也給每個圓增加一個黑色的邊框并為每個集合選擇不同的顏色。相對于計算每個氣泡顏色的漸變度Josh Marinacci采用先選擇一個基本的顏色然后改變它的透明度的方式為每個不同的氣泡選擇顏色。
var radgrad = ctx.createRadialGradient(
x-radius/10,y-radius/10,0,
x-radius/10,y-radius/10,radius+30);
radgrad.addColorStop(0, ’white’);
radgrad.addColorStop(0.5, ’white’);
radgrad.addColorStop(1, ’rgba(255,255,255,0.3)’);
ctx.globalAlpha = 0.6;
ctx.fillStyle = radgrad;
ctx.fill();
為了使圖表更加美觀上述代碼改變了字體增加了邊框,使用了一個來自于Subtle Patterns網站充滿娛樂性的背景。最終圖表如下:

總結
上面就是創(chuàng)建Bubble Chart的基本過程。利用上面的基本圖表你可以創(chuàng)建自己的Bubble Chart。你可以增加數(shù)據(jù)自動調整功能,讓圖表閱讀者自己選擇數(shù)據(jù)集合擴大或者縮小,你也可以在每個時間間隔中插入時間使時間劃分更細,動態(tài)過程更流暢。
你可以從這獲取工程源碼,也可以從作者的個人網站JoshOnDesign.com獲取
原文鏈接:
http://www.netmagazine.com/tutorials/create-interactive-bubble-chart-html5-canvas