Flutter Path(一) : Path 與 CustomPainter

Flutter程式設計開發發表於2020-04-26

Flutter Path(一) : Path  與 CustomPainter

Flutter 為我們提供了許多標準的元件可供我們在專案中使用,但是我們也常常需要自己去自定義 View,而自定義 View 最常見也是最基礎的就是使用 Path 了。

接下來正式開始學習 Path。

一、前期準備

先來看一段程式碼。

import 'package:flutter/material.dart';

void main() => runApp(
      MaterialApp(
        home: PathExample(),
      ),
    );

class PathExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: PathPainter(),
    );
  }
}

class PathPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 8.0;

    Path path = Path();
    // TODO: do operations here
    path.close();
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}
複製程式碼

在上面程式碼裡,我們主頁面是 StatelessWidget 型別的元件,在 build 方法裡面返回的是 CustomPaint 這個 Widget,而 CustomPaint 需要使用的 painter 是我們自定義的 PathPainter 。CustomPaint 這個元件為我們提供了一個畫布 (canvas),我們可以使用我們自定義的 CustomPainter 在這個畫布上把 paint() 這個方法裡面指定的內容繪製上去。

對於繪製有關的選項,我們可以通過 Paint 元件來進行設定,包括顏色、樣式、畫筆粗細等。

Paint paint = Paint()
   ..color = Colors.blueAccent             //畫筆顏色
   ..strokeCap = StrokeCap.round           //畫筆筆觸型別
   ..isAntiAlias = true                    //是否啟動抗鋸齒
   ..blendMode = BlendMode.exclusion       //顏色混合模式
   ..style = PaintingStyle.fill            //繪畫風格,預設為填充
   ..colorFilter = ColorFilter.mode(Colors.blueAccent,
       BlendMode.exclusion)                //顏色渲染模式
   ..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) //模糊遮罩效果
           ..filterQuality = FilterQuality.high //顏色渲染模式的質量
   ..strokeWidth = 5.0;                        //畫筆的寬度
複製程式碼

接下來我們就可以定義一個 Path 物件來決定畫什麼了。 Path 這個物件其實是一些列需要繪製的元素的集合,這些元素在繪製的時候都是根據一個起始點來繪製的(預設的 Path 的起始點是 (0,0) )。

最後需要使用 canvas 的 drawPath 方法來繪製 path ,這個方法需要兩個引數一個是 path 另一個是 paint 。

使用 Path 進行繪圖的基本流程差不多就是這些。對於手機上的座標系統,如下所示,左上角為座標原點。

Flutter Path(一) : Path  與 CustomPainter

二、moveTo

moveTo 方法就把繪製的七點移動到指定的位置。

  @override
 void paint(Canvas canvas, Size size) {
   Paint paint = Paint()
     ..color = Colors.red
     ..style = PaintingStyle.stroke
     ..strokeWidth = 8.0;

   Path path = Path();
   // 將起點移動到螢幕中心
   path.moveTo(size.width / 2, size.height / 2);
   canvas.drawPath(path, paint);
 }

複製程式碼

三、lineTo

lineTo 方法就是從起點繪製一條直線到 lineTo 裡面指定的一個點。

  @override
 void paint(Canvas canvas, Size size) {
   Paint paint = Paint()
     ..color = Colors.red
     ..style = PaintingStyle.stroke
     ..strokeWidth = 8.0;

   Path path = Path();
   // 從左上角起點到右下角終點
   path.lineTo(size.width, size.height);
   canvas.drawPath(path, paint);
 }
複製程式碼

Flutter Path(一) : Path  與 CustomPainter

四、quadraticBezierTo

quadraticBezierTo 是繪製二階貝塞爾曲線的。

Flutter Path(一) : Path  與 CustomPainter

從上圖可以看到,繪製貝塞爾曲線需要三個點,一個起點,一個控制點,一個終點。

@override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 8.0;

    Path path = Path();
    path.moveTo(0, size.height / 2);
    path.quadraticBezierTo(size.width / 2, size.height, size.width, size.height / 2);
    canvas.drawPath(path, paint);
  }
複製程式碼

Flutter Path(一) : Path  與 CustomPainter

五、cubicTo

cubicTo 是繪製三階貝塞爾曲線的。

Flutter Path(一) : Path  與 CustomPainter

三階貝塞爾需要兩個控制點。

 @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 8.0;
    
    Path path = Path();
    path.cubicTo(size.width / 4, 3 * size.height / 4, 3 * size.width / 4, size.height / 4, size.width, size.height);
    canvas.drawPath(path, paint);
  }
複製程式碼

Flutter Path(一) : Path  與 CustomPainter

六、conicTo

conicTo 方法也是繪製二次曲線的,和 quadraticBeizerTo 方法類似,但是這個方法主要是受到 weight 引數的控制。 當 weight 大於 1 時,繪製的是雙曲線,等於 1 時,繪製的是拋物線,小於1 時,繪製的是橢圓。

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 8.0;

    Path path = Path();
    path.conicTo(size.width / 4, 3 * size.height / 4, size.width, size.height, 20);
    canvas.drawPath(path, paint);
  }
複製程式碼

七、arcTo

arcTo 方法是繪製弧線的,方法原型如下:

void arcTo(Rect rect, double startAngle, double sweepAngle, bool forceMoveTo)
複製程式碼

需要四個引數:

  • rect: 圓弧所在矩形
  • startAngle : 開始弧度
  • sweepAngle : 需要繪製的弧度大小
  • forceMoveTo : 如果“forceMoveTo”引數為false,則新增一條直線段和一條弧段。 如果“forceMoveTo”引數為true,則啟動一個新的子路徑,其中包含一個弧段。
@override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 8.0;

    // Method to convert degree to radians
    num degToRad(num deg) => deg * (Math.pi / 180.0);

    Path path = Path();
    path.arcTo(Rect.fromLTWH(size.width / 2, size.height / 2, size.width / 4, size.height / 4), degToRad(0), degToRad(90), true);
    canvas.drawPath(path, paint);
  }
複製程式碼

Flutter Path(一) : Path  與 CustomPainter

八、addRect

繪製矩形。

  @override
 void paint(Canvas canvas, Size size) {
   Paint paint = Paint()
     ..color = Colors.red
     ..style = PaintingStyle.stroke
     ..strokeWidth = 8.0;

   Path path = Path();
   // Adds a rectangle
   path.addRect(Rect.fromLTWH(size.width / 2, size.height / 2, size.width / 4, size.height / 4));
   canvas.drawPath(path, paint);
 }
複製程式碼

Flutter Path(一) : Path  與 CustomPainter

九、addOval

繪製橢圓。

  @override
 void paint(Canvas canvas, Size size) {
   Paint paint = Paint()
     ..color = Colors.red
     ..style = PaintingStyle.stroke
     ..strokeWidth = 8.0;

   Path path = Path();
   // Adds an oval
   path.addOval(Rect.fromLTWH(size.width / 2, size.height / 2, size.width / 4, size.height / 4));
   canvas.drawPath(path, paint);
 }
複製程式碼

Flutter Path(一) : Path  與 CustomPainter

十、addArc

繪製弧線類似與 arcTo

 @override
 void paint(Canvas canvas, Size size) {
   Paint paint = Paint()
     ..color = Colors.red
     ..style = PaintingStyle.stroke
     ..strokeWidth = 8.0;

   // Method to convert degree to radians
   num degToRad(num deg) => deg * (Math.pi / 180.0);

   Path path = Path();
   // Adds a quarter arc
   path.addArc(Rect.fromLTWH(0, 0, size.width, size.height), degToRad(180), degToRad(90));
   canvas.drawPath(path, paint);
 }
複製程式碼

Flutter Path(一) : Path  與 CustomPainter

十一、addPolygon

繪製多邊形。可以指定多邊形的頂點,並且最後一個引數是 ture 時,最後一個點和第一個點會連線,多邊形閉合, false 時,不會閉合。

  @override
 void paint(Canvas canvas, Size size) {
   Paint paint = Paint()
     ..color = Colors.red
     ..style = PaintingStyle.stroke
     ..strokeWidth = 8.0;

   Path path = Path();
   // Adds a polygon from the starting point to quarter point of the screen and lastly
   // it will be in the bottom middle. Close method will draw a line between start and end.
   path.addPolygon([
     Offset.zero,
     Offset(size.width / 4, size.height / 4),
     Offset(size.width / 2, size.height)
   ], false);
   canvas.drawPath(path, paint);
 }
複製程式碼
  • false

Flutter Path(一) : Path  與 CustomPainter

  • true

Flutter Path(一) : Path  與 CustomPainter

十二、addRRect

繪製圓角矩形,圓角弧度由最後一個引數控制。

  @override
 void paint(Canvas canvas, Size size) {
   Paint paint = Paint()
     ..color = Colors.red
     ..style = PaintingStyle.stroke
     ..strokeWidth = 8.0;

   Path path = Path();
   path.addRRect(
     RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2, size.height / 2, size.width / 4, size.height / 4), Radius.circular(16))
   );
   canvas.drawPath(path, paint);
 }
複製程式碼

Flutter Path(一) : Path  與 CustomPainter

十三、path 實踐繪製進度條

class CircleProgressBarPainter extends CustomPainter {
 //背景
 Paint _paintBackground;

 //前景
 Paint _paintForeground;

 var currentValue;

 CircleProgressBarPainter(this.currentValue) {

   _paintBackground = Paint()
     ..color = Colors.blue
     ..strokeCap = StrokeCap.round
     ..style = PaintingStyle.stroke
     ..strokeWidth = 10.0
     ..isAntiAlias = true;

   _paintForeground = Paint()
     ..color = Colors.red
     ..strokeCap = StrokeCap.round
     ..style = PaintingStyle.stroke
     ..strokeWidth = 10.0
     ..isAntiAlias = true;

 }
 @override
 void paint(Canvas canvas, Size size) {

   //畫背景
   canvas.drawCircle(Offset(size.width / 2, size.height / 2), size.width / 2,
       _paintBackground);


   Rect rect = Rect.fromCircle(
     center: Offset(size.width / 2, size.height / 2),
     radius: size.width / 2,
   );

   //畫弧形進度
   canvas.drawArc(rect, 0.0, currentValue * Math.pi / 180, false, _paintForeground);
 }



 @override
 bool shouldRepaint(CustomPainter oldDelegate) {
   return false;
 }
}
複製程式碼

使用


   return Container(
     width: width,
     height: height,
     padding: EdgeInsets.all(20),
     child: CustomPaint(

         child: Center(
             child: Text(
                 (progressAnimation.value / 3.6).round().toString(),
             style: TextStyle(fontSize: 24,color: Colors.blue),
             ),
         ),

         painter: CircleProgressBarPainter(progressAnimation.value)

     ),

   );
複製程式碼

效果:

Flutter Path(一) : Path  與 CustomPainter

參考: medium.com/flutter-com…


github


歡迎關注「Flutter 程式設計開發」微信公眾號 。

Flutter Path(一) : Path  與 CustomPainter

相關文章