開源了一個圖表庫 lw-chart

前段時間遇到一個需求,須要畫一個 7日年化利率 的曲線圖。前端

UI 提了幾個需求,說實話,一開始看到時都懵逼了,而後我回了句 「你說,我不必定實現。」git

一開始看了網上的一些開源圖表,看是能實現,可是又以爲比較重,不必爲了一個圖表而引入整個庫。然後下定決心本身擼一個。github

固然了一開始並不打算寫一個 npm 庫,只是在項目中寫一個 js 來實現圖表繪製。在效果實現以後以爲須要整理一番,因而在五一期間進行了重構,將結構整清楚,提升擴展性,以便應對往後不一樣的需求。web

先來看看 demonpm

爲何叫 lw-chart

固然了 lw 不是我名字的縮寫,lw 是指 lightweight 輕量的意思,我但願這 lw-chart 可以真正作到輕量化,當我只使用其中某一個類型的圖表時,那麼只須要引入這個類型的圖表,其餘的不相關的代碼不要來增長項目的體積。canvas

lw-chart 是如何作的呢?api

lw-chart 內部經過類繼承的方式提升代碼的複用性,同時在編譯打包時,輸出多個文件,當實際項目中使用了其中某一個類型的圖表,那麼能夠再項目中只 import 一個文件,避免把全部類型的圖表都都入到項目中而不使用的狀況。數組

具體的使用方式能夠查看文檔,這裏就不在贅述。編輯器

踩過的坑

佈局

在開始編碼前必定要先考慮好佈局,否則在寫代碼時會很混亂,不知道座標該加仍是該減。函數

第一次寫的時候,由於參考了網上的代碼,致使沒有清晰的佈局,雖然能繪製出一些東西,可是遇到要計算座標時,就會很混亂,致使就都沒法達到預期的效果。

最後本身畫了一個佈局的圖,再開始編碼。下面是個人一個佈局結構

lw-chart
lw-chart

我分了兩個類來實現這個佈局,一個是基類 LWChart,定義了 canvastitleBarchart

而 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 <= 0return [];
  // 長度比例係數
  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 繪製出來的爲直線,因此須要小間隙繪製多個點才能使得曲線相對平滑。

動畫時間計算

首先經過計算每段曲線應該執行的時間, 結合 requestAnimationFrameperformance.now(); 獲取每一幀的時間差,經過時間差與每一段曲線的單位時間進行對比,計算出百分比,再經過上面的 for 循環進行繪製。

拓展

目前 lw-chart 中實現的圖表繪製的只有 Area,能夠經過參數配置控制顯示線條或區域。可是這也許仍是仍是不夠好用,也不可能知足到各個需求。

因此各位開發者能夠在 lw-chart 的基礎上進行定製開發,經過繼承 LWChart 或者 Axis 這個類。

關於如何開發,後期整理後會在文檔上更新。

歡迎各位大佬 pull request,一同開發。


若是以爲 lw-chart 不錯的話,不妨下載體驗一番,順便到 Github 點個 Star。

若是以爲內容還不錯的話,但願小夥伴能夠幫忙點贊轉發,給更多的同窗看到,感謝感謝!

若是你喜歡個人文章,還能夠關注個人公衆號【前端develop】

前端develop
前端develop
相關文章
相關標籤/搜索