資料視覺化實踐

B_Cornelius發表於2018-10-25

資料視覺化的目的其實就是直觀地展現資料,例如讓花費數小時甚至更久才能歸納的資料量,轉化成一眼就能讀懂的指標;通過加減乘除、各類公式權衡計算得到的兩組資料差異,在圖中顏色敏感、長短大小即能形成對比;資料視覺化是一個溝通複雜資訊的強大武器。通過視覺化資訊,我們的大腦能夠更好地抓取和儲存有效資訊,增加資訊的印象。但如果資料視覺化做的較弱,反而會帶來負面效果;錯誤的表達往往會損害資料的傳播,完全曲解和誤導使用者,所以更需要我們多維的展現資料,就不僅僅是單一層面, 目前有多種第三方庫來實現資料的視覺化:Highcharts, Echarts, Chart.js, D3.js等。
總的來說,現在的第三方庫都是基於這兩種瀏覽器圖形渲染技術實現的:Canvas和SVG。canvas和webGL都是基於openGL來進行封裝。但是webGL由於更貼近openGL所以學習曲線較陡,這裡就講解Canvas和SVG兩種。 下面是兩種圖形渲染技術的對比

SVG Canvas
不依賴解析度 依賴解析度
支援事件處理器 不支援事件處理器
最適合帶有大型渲染區域的應用程式 弱的文字渲染能力
複雜度高會減慢渲染速度(任何過度使用 DOM 的應用都不快) 能夠以 .png 或 .jpg 格式儲存結果影象
不適合遊戲應用 最適合圖影象密集型的遊戲
可以為某個元素附加 JavaScript 事件處理器。在 SVG 中,每個被繪製的圖形均被視為物件。 一旦圖形被繪製完成,它就不會繼續得到瀏覽器的關注。如果其位置發生變化,那麼整個場景都需要重新繪製。

Echarts

是百度的一個開源的資料視覺化工具,一個純 Javascript 的圖表庫,能夠在 PC 端和移動裝置上流暢執行,相容當前絕大部分瀏覽器(IE6/7/8/9/10/11,chrome,firefox,Safari等),底層依賴輕量級的 Canvas 庫 ZRender,ECharts 提供直觀,生動,可互動,可高度個性化定製的資料視覺化圖表。下面是簡單的使用方法:

option = {
    xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
        type: 'value'
    },
    series: [{
        data: [820, 932, 901, 934, 1290, 1330, 1320],
        type: 'line'
    }]
};
複製程式碼

Echarts

如何支援多種渲染方式

echarts支援svg、canvas、vml等底層技術 echarts會根據具體的渲染平臺去做不同的渲染實現,底層是一個叫路徑代理PathProxy的類,它會負責這個底層的繪製指令。根據不同的渲染器,底層進行不同的實現。

const rect = new zrender.Rect({
  shape: {
    x: 10,
    y: 10,
    width: 80,
    height: 80
  }
});
複製程式碼

如何支援事件處理

在canvas不能夠為某個元素繫結事件,所以採用給整個圖表容器繫結事件。當進行事件處理的時候,先判斷滑鼠是否在圖形以內。因為圖形經過旋轉和縮放,所以需要將滑鼠座標切換到圖形座標系。獲取到圖形座標系以後,就可以知道滑鼠和圖形之間的關係,就可以進行相應的事件處理。

SVG渲染如何部分更新

在渲染的時候canvas一旦改變就完全重繪,但是效率很高。對於SVG而言,假如說散點圖有一千個,那麼DOM就有一千個節點。如果每一幀都需要把DOM元素進行刪除然後新增,效率是非常低的。所以這裡可是使用virtual-Dom的方法,通過維護一個渲染物件列表,每幀將新的渲染物件列表與上一幀的進行diff,獲得新增、修改、刪除的渲染物件列表,在根據列表對DOM相關節點進行調整。

資料視覺化的具體實現

這裡基於兩種實現方式,一種canvas一種svg。

Canvas

在這裡實現了一個簡單的庫,可以繪製柱狀圖、餅狀圖、折線圖、雷達圖。 下面是使用方法:

const canvas = document.getElementById('canvas');
const data = [{
	name: '籃球',
	value: 2260,
},
{
	name: '羽毛球',
	value: 1170,
},
{
	name: '乒乓球',
	value: 1230,
},
{
	name: '足球',
	value: 1450,
},
];
const settings = {
	type: 'bar'
};
new Chart(canvas, data, {
	title: 'Sport'
}, settings);
複製程式碼

下面是效果圖

資料視覺化實踐
建構函式可以傳入的引數有四個,canvas畫布物件,data是我們傳入的資料物件,settings是傳入的配置,就可以定義圖形的型別,可以是柱狀圖,或者折線圖。在這裡根據傳入的type進行相應的繪圖更改。內部的實現原理如下:

if (settings) {
  Object.keys(settings).map((key) => {
    this[key] = settings[key];
  });
}
複製程式碼

上面這個部分就能夠讓傳入的引數覆蓋已有的預設設定,顏色,座標。有些設定需要通過計算才能獲得,例如每個單位長度的標記,獲取每個值的比例。例如:

this.totalValue = this.getTotalValue();
this.maxValue = this.getMaxValue();
function getTotalValue() {
  let total = 0;
  this.data.map((item) => {
    total += item.value;
  });
  return total;
}
複製程式碼

這裡先計算出總數,然後繪製餅狀圖的時候就計算出每一條資料所佔的比例,進行繪圖。 下面這部分會根據傳入的type來繪製不同的圖形,下面是具體實現:

if (this.type === 'bar' || this.type === 'line') {
  this.drawBarUpdate();
} else if (this.type === 'pie' || this.type === 'ring') {
  this.drawPieUpdate();
} else if (this.type === 'radar') {
  this.drawRadarUpdate();
}
複製程式碼

draBarUpdate的具體實現:

  drawBarUpdate() {
    this.drawAxis();
    this.drawPoint();
    this.drawTitle();
    this.drawBarChart();
  }
複製程式碼

前三個函式用於基本的結構,軸、點、標題。第四個函式用來繪製圖形。主要藉助的是canvas的幾種方法fillStyle:設定填充繪畫的顏色、漸變或模式;strokeStyle: 設定筆觸的顏色、漸變或模型;beginPath:開始一條路徑,或者重置當前的路徑;arc: 用來建立弧/曲線arc(x, y, r, startAngle, endAngle, direction)x,y分別代表圓中心的x,y座標。startAngle為起始角,endAngle為結束角,direction代表順時針,還是逆時針繪圖。首先根據資料的長度,確定每條資料的長度和座標然後使用下面操作進行繪圖。

this.ctx.beginPath();
this.ctx.arc(x, y, radius, startAngle, endAngle, direction);
this.ctx.fill();
複製程式碼

這樣就可以繪製圖形了

SVG

SVG是一種使用XML描述2D圖形的語言。SVG基於XML,這意味著SVG DOM的每個元素都是可用的。你可以為每個元素附加javaScript事件處理器。 svg不同於canvas,提供了很多基本形狀。例如rect: 圓;circle: 橢圓;ellipse:直線;line: 折線;polyline:多邊形;polygon:路徑。 這裡藉助common模組下的pie.js進行了解。 下面是使用方法:

var myPie = new Pie({
  pieR: 40, // 外徑
  donutR: 35, // 內徑
  rotation: -90, // 旋轉到從y軸正方向起始
  strokeColor: '#FFF', // 使用白色描邊
  animation: true, // 啟用預設展現動畫
  slices: [{
    color: '#E3E3E3', // 第一切片顏色
    percent: 0.1 // 第一切片面積佔比
  }, {
    color: '#5FC2F5', // 第二切片顏色
    percent: 0.2 // 第二切片面積佔比
  }, {
    percent: 0.3 // 第三切片面積佔比
  }, {
    percent: 0.4 // 第四切片面積佔比
  }]
});

$('body').append(myPie.getNode()); // 插入餅圖。
複製程式碼

下面是效果圖

資料視覺化實踐
首先也是進行重置預設的引數設定,還有計算一些屬性,例

this.args = $.extend({
  pieR: 100,
  slices: [{
    percent: 1,
  }],
}, args);
$.each(this.args.slices, function(i, item) {
  item.angle = (item.percent || 0) * 360;
})
複製程式碼

然後設定每個元素應該設定的路徑,是通過下面函式進行實現:

 /**
  *  @param {Number} startAngle 開始的角度
  *  @param {Number} angle 旋轉角度
  *  @param {Number} pieR 半徑
  *  @param {Number} donutR 環形圖所需要的引數
  *  @return {Object} 座標物件
  */
getSectorPath(startAngle, angle, pieR, donutR) {
  startAngle = startAngle * Math.PI / 180;
  angle = angle * Math.PI / 180;

  var startAngleTri = {
    cos: Math.cos(startAngle),
    sin: Math.sin(startAngle)
  };

  var angleTri = {
    cos: Math.cos(startAngle + angle),
    sin: Math.sin(startAngle + angle)
  };

  return [
    'M', donutR * startAngleTri.cos, donutR * startAngleTri.sin, // 開始點
    'L', pieR * startAngleTri.cos, pieR * startAngleTri.sin, // 開始的邊界
    'A', pieR, pieR, // 外部的半徑
    0, // x軸上的旋轉
    Math.abs(angle) > Math.PI ? 1 : 0, // large-arc-flag
    1, // sweep-flag
    pieR * angleTri.cos, pieR * angleTri.sin, // end point
    'L', donutR * angleTri.cos, donutR * angleTri.sin, // end edge
    'A', donutR, donutR, // inner arc
    0, // x軸上的旋轉
    Math.abs(angle) > Math.PI ? 1 : 0, // large-arc-flag
    0, // sweep-flag
    donutR * startAngleTri.cos, donutR * startAngleTri.sin // 結束點
  ].join(' ');
}
複製程式碼

繪製圖形是靠的path這個元素,下面的命令可以用於路徑資料:

M = moveto
L = lineto
H = horizontal lineto
V = vertical lineto
C = curveto
S = smooth curveto
Q = quadratic Belzier curve
T = smooth quadratic Belzier curveto
A = elliptical Arc
Z = closepath
複製程式碼

簡便寫就可以如下:

<path d="M250 150 L150 350 L350 350 Z" />
複製程式碼

那麼繪製餅狀圖的就可以如下這麼寫:

$(path).attr({
  'd': getSectorPath(startAngle, angle, pieR, donutR)
}).css({
  // 屬性
})
複製程式碼

這就是svg的繪畫方式,相對canvas繪畫,svg因為提供了一些基本的圖形元件所以更好畫,但是各有優點 。 如果需要製作更好的圖形庫,我們就需要藉助繪圖引擎,就能夠針對多種平臺使用不同的渲染方式。

相關文章