在flutter中圖表框架目前相對較少,且不太符合專案需求,本篇文章將介紹如何運用自定義view完成曲線統計圖的繪製,最終效果如圖.
繪製背景線條
/// 畫背景線
void darwBackgroundLine(Canvas canvas) {
painter.strokeWidth = 1.2; // 線條寬度
painter.color = Color(0xffEBECF0);
for (int i = 0; i < LINE_NUM - 1; i++) {
// 繪製橫線
canvas.drawLine(Offset(yTextwidth, i * LINE_SPACE + Y_TEXT_HEIGHT), Offset(width, i * LINE_SPACE + Y_TEXT_HEIGHT), painter);
}
painter.strokeWidth = 1.4; // 最後一條線條較粗
painter.color = Color(0xffC4C6CF);
// 繪製橫線
canvas.drawLine(Offset(yTextwidth, (LINE_NUM - 1) * LINE_SPACE + Y_TEXT_HEIGHT),
Offset(width, (LINE_NUM - 1) * LINE_SPACE + Y_TEXT_HEIGHT), painter);
}
複製程式碼
繪製文字,X,Y值及標題
/// 座標抽文字
void xyText(Canvas canvas) {
// y軸標題
textPainter.text = TextSpan(
text: lineChartBean.yRemindText,
style: new TextStyle(
color: Color(0xff303133),
fontSize: S(20),
),
);
textPainter.layout();
textPainter.paint(canvas, Offset(0, 0));
// y 軸文字
for (int i = 0; i < LINE_NUM; i++) {
textPainter.text = TextSpan(
text: '${(LINE_NUM - i - 1) * 1000}',
style: new TextStyle(
color: Colors.black,
fontSize: S(24),
),
);
textPainter.layout();
textPainter.paint(canvas,
Offset((yTextwidth - textPainter.width) / 2, i * LINE_SPACE - textPainter.height / 2 + Y_TEXT_HEIGHT));
}
double xTextWidth = width - yTextwidth;
var temp = xTextWidth / 4;
// x 文字
for (int i = 1; i < 5; i++) {
textPainter.text = TextSpan(
text: '$i 月',
style: new TextStyle(
color: Colors.black,
fontSize: 12,
),
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(yTextwidth + temp * i - temp / 2 - textPainter.width / 2,
(LINE_NUM - 1) * LINE_SPACE + 6 + Y_TEXT_HEIGHT));
}
}
複製程式碼
繪製曲線
在flutter中可以通過Path繪製線條,把已知的四個點連起來,就是基本的折線統計圖,但要繪製出點與點之前平滑過渡的曲線,就需要另外一個方法得到曲線的座標,這個方法就是cos函式.
做之前先回憶一下cos長啥樣.
繪製所需要的就是0 ~ 2π 間的曲線.
/// 獲取曲線路徑
/// [v1] 第一個Y軸值 [v2] 第二個Y軸值
/// [index] X軸座標位置
Path getCurvePath(double v1, double v2, int index, Path path) {
int clipNum = 30; // 一段曲線被分割繪製的數量,越大麴線越平滑.
double temp = 1 / clipNum; // 遍歷運算用到的臨時數值
bool isNegativeNumber; // 是否為負數
double diff = (v1 - v2).abs(); // 兩點之間的差值
isNegativeNumber = (v1 - v2) < 0; // 判斷正負
for (int i = 0; i < clipNum; i++) {
path.lineTo(
// x點座標值,x軸不參與cos運算
getX(temp * i + index.toDouble()),
// y點座標值
// 公式 y = cos(v) + 1 , isNegativeNumber 為true時,用到的是π~2π之間的曲線,為false時,用到的是0~π間的曲線.
// 通過公式運算之後再與實際大小做比相乘得出實際結果,新增到Path
getY((cos((isNegativeNumber ? pi : 0) + pi * temp * i) + 1) * diff / 2 + (isNegativeNumber ? v1 : v2)));
}
// 返回Path
return path;
}
/// 獲取Y軸座標
double getY(double value) => (MAX_VALUE - value) / MAX_VALUE * (LINE_NUM - 1) * LINE_SPACE + Y_TEXT_HEIGHT;
/// 獲取X軸座標
double getX(double index) => yTextwidth + unitWidth / 2 + index * unitWidth;
複製程式碼
// 最後用drawPath完成整段繪製.
canvas.drawPath(path, painter);
複製程式碼
最後
以上為全部內容,如有錯誤請指正.