原發於知乎:
zhuanlan.zhihu.com/p/32740553
引言, 爲何想要研究 Chartjs
繼以前咱們研究了SVG.js 和 Frappe Charts 後, 咱們對於 svg 的圖表庫已經有了初步的瞭解, 可是對於可視化世界的 canvas, 咱們更應該投入精力去了解學習.
在看到 chartist.js 講到本身的優點的時候, 提到一些圖表庫使用了錯誤的技術 canvas, 那咱們就更有興趣去了解, 爲何會有這種說法. 首先讓咱們一塊兒來了解一下 Chartjs.
Chartjs 介紹
Chartjs 的官方介紹是一個簡單靈活的圖表庫, 相對而言 Chartjs 在圖表庫中的優點, 主要是配置簡單, 動畫比較優雅, 而基於 canvas 的特性, 讓 Chartjs 性能會更有優點. Chartjs 目前擁有 34.4 K的 star, 幾乎已是 canvas 版本的圖表代名詞, 也是最流行的基於 canvas 的圖表庫. 在 GitHub 上搜索 chart, 能夠看到 Chartjs 的流行度排名僅次於 D3.
<canvas id="myChart"></canvas> 複製代碼
var ctx = document.getElementById('myChart').getContext("2d");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL"],
datasets: [{
label: "Data",
borderColor: "#80b6f4",
fill: false,
data: [50, 120, 150, 170, 180, 170, 160]
}]
}
});複製代碼
Chart.js 代碼組織方式
Chart.js 圖表建立過程
從源文件的 core.controller.js 分析, 來看 Chartjs 初始化圖表的過程以下:
Chartjs 插件機制
Chartjs 的插件機制看起來很簡單, 可是也頗有效. 插件直接註冊到 plugin 裏面, 擁有所有的執行的生命週期, 並且能夠直接訪問 Chart 的全局變量, 擁有全部 API 的訪問權限.
Chart.plugins.register({
beforeInit() {}
afterInit() {}
afterUpdate() {}
afterLayout() {}
afterDatasetsUpdate() {}
afterDatasetUpdate() {}
afterRender() {}
afterDraw() {}
afterDatasetsDraw() {}
afterDatasetDraw() {}
afterEvent() {}
resize() {}
destroy() {}
});複製代碼
Chartjs 鼠標事件和動畫
對於 canvas 類型的圖表而言, 處理相應的鼠標時間一直是件比較麻煩的事情. 讓咱們來看下 Chartjs 是怎麼作的吧? 從源文件的core.controller.js 文件的 handleEvent 能夠看出, Chartjs 在根元素位置, 監聽對應的鼠標事件, 而後經過以前記錄的元素位置, 找到最近的對應元素, 響應對應的事件.
if (e.type === 'mouseout') {
me.active = [];
} else {
me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
}
...
me.updateHoverStyle(me.active, hoverOptions.mode, true);複製代碼
從代碼中能夠看出, Chartjs 是經過 getElementsAtEventForMode 方法去獲取對應的元素. Chartjs 提供了 6 種模式, 來響應交互. 咱們一塊兒來看一下這六種模式.
-
point: 找到 鼠標位置相交的 對應元素
-
nearest: 找到對應距離最近的元素
-
index: 根據位置, 找到不一樣數據集中 對應 index 的數據
-
dataset: 根據位置, 找到只在同一數據集的元素
-
x: 只根據鼠標位置的 x 軸值, 找到與 x 軸值相交的元素, 適應於垂直光標的場景
-
y: 只根據鼠標位置的 y 軸值, 找到與 y 軸值相交的元素, 適應於垂直光標的場景
而對於動畫, 在core.animation.js文件的實現了對於 animation 的堆棧, 針對動畫依次使用 requestAnimationFrame 進行動畫的調用. 動畫中也內置了常見的各類緩動函數, 用於常見的動畫效果. 咱們能夠看一下動畫的核心實現, 裏面的 advance 方法.
while (i < animations.length) {
animation = animations[i];
chart = animation.chart;
animation.currentStep = (animation.currentStep || 0) + count;
animation.currentStep = Math.min(animation.currentStep, animation.numSteps);
helpers.callback(animation.render, [chart, animation], chart);
helpers.callback(animation.onAnimationProgress, [animation], chart);
if (animation.currentStep >= animation.numSteps) {
helpers.callback(animation.onAnimationComplete, [animation], chart);
chart.animating = false;
animations.splice(i, 1);
} else {
++i;
}
}複製代碼
上述代碼中的, animation.render 根據動畫中的當前動畫的進度, 來繪製出動畫所涉及元素的中間狀態.
Chartjs 浮點數問題
在閱讀 Chartjs 源碼的過程當中, 發現源碼部分沒有針對浮點數問題作任何處理, 不少地方也都沒有考慮過浮點數問題. 因此 Chartjs 在使用過程當中可能會有以下的問題:
One more thing
在使用不少通用圖表的時候, 相信你們都會遇到通用圖表的定製化困難這種問題, 下期咱們將分析一下
可視化圖形語法G2, 看下 G2 是怎麼實現對於圖表的高度的易用性和擴展性.
在看 Chartjs源碼的同時, 咱們也動手用 canvas 實踐了一些基礎圖表, 具體能夠參見
Taco.