前段時間遇到一個需求,須要畫一個 7日年化利率 的曲線圖。前端
UI 提了幾個需求,說實話,一開始看到時都懵逼了,而後我回了句 「你說,我不必定實現。」git
一開始看了網上的一些開源圖表,看是能實現,可是又以爲比較重,不必爲了一個圖表而引入整個庫。然後下定決心本身擼一個。github
固然了一開始並不打算寫一個 npm 庫,只是在項目中寫一個 js 來實現圖表繪製。在效果實現以後以爲須要整理一番,因而在五一期間進行了重構,將結構整清楚,提升擴展性,以便應對往後不一樣的需求。web
先來看看 demonpm
固然了 lw 不是我名字的縮寫,lw 是指 lightweight 輕量的意思,我但願這 lw-chart 可以真正作到輕量化,當我只使用其中某一個類型的圖表時,那麼只須要引入這個類型的圖表,其餘的不相關的代碼不要來增長項目的體積。canvas
「lw-chart 是如何作的呢?」api
lw-chart 內部經過類繼承的方式提升代碼的複用性,同時在編譯打包時,輸出多個文件,當實際項目中使用了其中某一個類型的圖表,那麼能夠再項目中只 import
一個文件,避免把全部類型的圖表都都入到項目中而不使用的狀況。數組
具體的使用方式能夠查看文檔,這裏就不在贅述。markdown
在開始編碼前必定要先考慮好佈局,否則在寫代碼時會很混亂,不知道座標該加仍是該減。函數
第一次寫的時候,由於參考了網上的代碼,致使沒有清晰的佈局,雖然能繪製出一些東西,可是遇到要計算座標時,就會很混亂,致使就都沒法達到預期的效果。
最後本身畫了一個佈局的圖,再開始編碼。下面是個人一個佈局結構
我分了兩個類來實現這個佈局,一個是基類 LWChart
,定義了 canvas
,titleBar
,chart
。
而 x座標 和 y座標 則定義在 Axis
座標軸類中,由於有些圖表可能不須要座標軸,因此經過 Axis
繼承 LWChart
達到更靈活的配置。
獲取屏幕 dpi,尺寸及位置參數在繪製時乘以 dpi
。
dpi
的計算方式以下:
let n = (ctx.backingStorePixelRatio ||
ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio ||
1);
let a = (window.devicePixelRatio || 1) / n;
if (a < 1) {
a = 1;
}
const dpi = a / n;
複製代碼
可能大部分都是使用現成的圖表庫,網上比較少關於貝塞爾曲線控制點計算的文章。
/** 給原始座標點添加三次貝塞爾曲線控制點 */
export const getCurveList = function (pointList: IPos[]): ICurvePoint[] {
if (pointList.length <= 0) return [];
// 長度比例係數
const lenParam = 1 / 3;
const len = pointList.length;
return pointList.map((curPoint, index) => {
const nextPoint = index === len - 1 ? curPoint : pointList[index + 1];
const curX = curPoint.x;
const curY = curPoint.y;
const nextX = nextPoint.x;
const nextY = nextPoint.y;
const deltaX = Math.abs(nextX - curX) * lenParam;
return {
start: curPoint,
end: nextPoint,
control1: {
x: curX + deltaX,
y: curY
},
control2: {
x: nextX - deltaX,
y: nextY
}
};
});
};
複製代碼
以上是個人計算方式,主要經過先後兩個點的 x座標 進行加減生成兩個控制點,使得貝塞爾曲線相對平滑。
動畫主要經過 requestAnimationFrame
api 進行實現。
將須要繪製的數據存入一個數組中,將數組中的前 n - 1 個數據使用 bezierCurveTo
繪製,最後一段曲線使用貝塞爾曲線函數進行繪製
// 動畫執行中,使用貝塞爾函數繪製最後一段曲線
for (let t = 0; t <= percent / 100; t += 0.1) {
lastX = curveWithTime(lastPoint.start.x, lastPoint.control1.x, lastPoint.control2.x, lastPoint.end.x, t);
lastY = curveWithTime(lastPoint.start.y, lastPoint.control1.y, lastPoint.control2.y, lastPoint.end.y, t);
this.ctx.lineTo(lastX, lastY);
}
複製代碼
使用 ctx.lineTo
繪製出來的爲直線,因此須要小間隙繪製多個點才能使得曲線相對平滑。
首先經過計算每段曲線應該執行的時間, 結合 requestAnimationFrame
和 performance.now();
獲取每一幀的時間差,經過時間差與每一段曲線的單位時間進行對比,計算出百分比,再經過上面的 for
循環進行繪製。
目前 lw-chart 中實現的圖表繪製的只有 Area
,能夠經過參數配置控制顯示線條或區域。可是這也許仍是仍是不夠好用,也不可能知足到各個需求。
因此各位開發者能夠在 lw-chart 的基礎上進行定製開發,經過繼承 LWChart
或者 Axis
這個類。
關於如何開發,後期整理後會在文檔上更新。
歡迎各位大佬 pull request,一同開發。
若是以爲 lw-chart 不錯的話,不妨下載體驗一番,順便到 Github 點個 Star。
若是以爲內容還不錯的話,但願小夥伴能夠幫忙點贊轉發,給更多的同窗看到,感謝感謝!
若是你喜歡個人文章,還能夠關注個人公衆號【前端develop】