開源了一個圖表庫 lw-chart

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

UI 提了幾個需求,說實話,一開始看到時都懵逼了,然後我回了句 “你說,我不一定實現。”

一開始看了網上的一些開源圖表,看是能實現,但是又覺得比較重,沒必要爲了一個圖表而引入整個庫。而後下定決心自己擼一個。

當然了一開始並不打算寫一個 npm 庫,只是在項目中寫一個 js 來實現圖表繪製。在效果實現之後覺得需要整理一番,於是在五一期間進行了重構,將結構整清楚,提高擴展性,以便應對日後不同的需求。

先來看看 demo

爲什麼叫 lw-chart

當然了 lw 不是我名字的縮寫,lw 是指 lightweight 輕量的意思,我希望這 lw-chart 能夠真正做到輕量化,當我只使用其中某一個類型的圖表時,那麼只需要引入這個類型的圖表,其他的不相關的代碼不要來增加項目的體積。

lw-chart 是如何做的呢?

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

具體的使用方式可以查看文檔,這裏就不在贅述。

踩過的坑

佈局

在開始編碼前一定要先考慮好佈局,不然在寫代碼時會很混亂,不知道座標該加還是該減。

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

最後自己畫了一個佈局的圖,再開始編碼。下面是我的一個佈局結構

我分了兩個類來實現這個佈局,一個是基類 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 <= 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 繪製出來的爲直線,所以需要小間隙繪製多個點才能使得曲線相對平滑。

動畫時間計算

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

拓展

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

所以各位開發者可以在 lw-chart 的基礎上進行定製開發。通過繼承 LWChart 或者 Axis 這個類。

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

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


如果覺得 lw-chart 不錯的話,不妨下載體驗一番,順便到 Github 點個 Star。

如果覺得內容還不錯的話,希望小夥伴可以幫忙點贊轉發,給更多的同學看到,感謝感謝!

如果你喜歡我的文章,還可以關注我的公衆號【前端develop】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章