【Flutter學習】之繪畫實例(一)


一,概述

  • 畫布Canvas

    畫布是一個矩形區域,我們可以控制其每一像素來繪制我們想要的內容

    Canvas 擁有多種繪制點路徑矩形圓形、以及添加圖像等的方法,結合這些方法我們可以繪制出千變萬化的畫面。

    Canvas中有多個與繪制相關的方法,如drawLine()drawRect()drawOval()drawOval()、等方法。

  • 畫筆Paint

    雖然,畫布可以畫路徑矩形圓形等,但是決定這些圖形顏色粗細表現的還是畫筆
    Paint非常好理解,就是我們用來畫圖形的工具,我們可以設置畫筆的顏色粗細、是否抗鋸齒筆觸形狀以及作畫風格

    通過這些屬性我們可以很方便的來定制自己的UI效果,當然我們在“作畫”的過程中可以定義多個畫筆,這樣更方便我們對圖形的繪制

    畫筆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)///顏色渲染模式,一般是矩陣效果來改變的,但是flutter中只能使用顏色混合模式
      ..maskFilter = MaskFilter.blur(BlurStyle.inner,3.0)//模糊遮罩效果
      ..filterQuality = FilterQuality.high //顏色渲染模式質量
      ..strokeWidth = 15.0; //畫筆的寬度

二,繪制實例

  • 自定義組件

      Flutter中如果想要自定義繪制,那么你需要用到 CustomPaint 和 CustomPainter ;  CustomPaint是Widget的子類。
    • 構造方法
      const CustomPaint({
          Key key,
          this.painter,
          this.foregroundPainter,
          this.size = Size.zero,
          this.isComplex = false,
          this.willChange = false,
          Widget child,
        }) :super(key: key, child: child);

        我們只需要關心三個參數,painterforegroundPainterchild  , 這里需要說明一下,painter 是繪制的 backgroud 層,而child 是在backgroud之上繪制,foregroundPainter 是在 child 之上繪制,所以這里就有了個層級關系,這跟android里面的backgroudforeground是一個意思,那這兩個painter的應用場景是什么呢?假如你只是單純的想繪制一個圖形,只用painter就可以了,但是如果你想給繪制區域添加一個背景(顏色圖片,等等),這時候如果使用 painter是會有問題的,painter的繪制會被child 層覆蓋掉,此時你只需要將painter替換成foregroundPainter,然會顏色或者圖片傳遞給child即可。

            如果是Android繪制幾何圖形,應該是重寫ViewonLayout()onDraw方法,但是Flutter實現繪制,必須繼承CustomPainter並重寫 paintCanvascanvas, Size size)和 shouldRepaint (CustomPainteroldDelegate) 方法 ,第一個參數canvas就是我們繪制的畫布了(跟Android一模一樣),paint第二個參數Size就是上面CustomPaint構造方法傳入的size, 決定繪制區域的寬高信息

             既然Size已經確定了,現在就定義下繪制區域的邊界,一般我做類似的UI,都會定義一個最基本的padding, 一般取值為16 , 因為繪制的內容與坐標軸之間需要找到一個基准線,這樣更容易繪制,而且調試邊距也很靈活

    • 工程構建
      import 'package:flutter/material.dart';
      
      void main() => runApp(MyApp());
      
      class MyApp extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: MyHomePage(title: 'Painter繪制直線'),
          );
        }
      }
      
      class MyHomePage extends StatefulWidget {
        MyHomePage({Key key, this.title}) : super(key: key);
        final String title;
        @override
        _MyHomePageState createState() => _MyHomePageState();
      }
      
      class _MyHomePageState extends State<MyHomePage> {
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            appBar: AppBar(
              title: Text(widget.title),
            ),
            body: Center(
              child: CustomPaint(
                size: Size(300, 300),
                painter:MyPainter() ,
              ),
            )
          );
        }
      }
      
      class MyPainter extends CustomPainter {
        ///[定義畫筆]
        Paint _paint = Paint()
        ..color = Colors.blueAccent //畫筆顏色
        ..strokeCap = StrokeCap.round//畫筆筆觸類型
        ..isAntiAlias = true //是否啟動抗鋸齒
        ..style = PaintingStyle.fill //繪畫風格,默認為填充
        ..strokeWidth = 5.0; //畫筆的寬度
      
        @override
        void paint(Canvas canvas, Size size) {
           canvas.drawLine(Offset(20, 20), Offset(100,100), _paint);
        }
      
        @override
        bool shouldRepaint(CustomPainter oldDelegate) {
          return null;
        }
      }
  • 定義畫筆

    ///[定義畫筆]
      Paint _paint = Paint()
      ..color = Colors.blueAccent //畫筆顏色
      ..strokeCap = StrokeCap.round//畫筆筆觸類型
      ..isAntiAlias = true //是否啟動抗鋸齒
      ..style = PaintingStyle.fill //繪畫風格,默認為填充
      ..strokeWidth = 5.0; //畫筆的寬度
  • 內容繪制

    • 繪制直線(drawLine)
      使用給定的塗料在給定點之間繪制一條線。 該行被描邊,此調用忽略[Paint.style]的值。p1p2參數為兩個點的坐標 , 在這兩點之間繪制一條直線。
      • 系統方法
        void drawLine(Offset p1, Offset p2, Paint paint)
      • 使用方法

        class MyPainter extends CustomPainter {
        ///[定義畫筆]
        Paint _paint = Paint()
        ..color = Colors.blueAccent //畫筆顏色
        ..strokeCap = StrokeCap.round//畫筆筆觸類型
        ..isAntiAlias = true //是否啟動抗鋸齒
        ..style = PaintingStyle.fill //繪畫風格,默認為填充
        ..strokeWidth = 5.0; //畫筆的寬度

        @override
        void paint(Canvas canvas, Size size) {
         canvas.drawLine(Offset(20, 20), Offset(100,100), _paint);
        }

        @override
        bool shouldRepaint(CustomPainter oldDelegate) {
        return null;
        }
        }
      • 效果示例

    • 繪制點(drawPoints)
      繪制點也是非常的簡單,3個參數分別為: PointMode枚舉,坐標 list 和 paint。PointMode的枚舉類型有三個,points(點),lines(線,隔點連接),polygon(線,相鄰連接)
      • 系統方法
        void drawPoints(PointMode pointMode, List points, Paint paint)
      • 使用方法

        class MyPainter extends CustomPainter {
          
          ///[定義畫筆]
          Paint _paint = Paint()
          ..color = Colors.blueAccent //畫筆顏色
          ..strokeCap = StrokeCap.round//畫筆筆觸類型
          ..isAntiAlias = true //是否啟動抗鋸齒
          ..style = PaintingStyle.fill //繪畫風格,默認為填充
          ..strokeWidth = 5.0; //畫筆的寬度
        
          @override
          void paint(Canvas canvas, Size size) {
            ///PointMode的枚舉類型有三個,points(點),lines(線,隔點連接),polygon(線,相鄰連接)
             canvas.drawPoints(
               PointMode.points,
               [
                 Offset(20.0,  40.0),
                 Offset(100.0, 120.0),
                 Offset(100.0, 220.0),
                 Offset(200.0, 220.0),
                 Offset(200.0, 120.0),
                 Offset(280.0, 40.0),
                 Offset(20,    40.0),
               ], 
               _paint
               );
          }
        
          @override
          bool shouldRepaint(CustomPainter oldDelegate) {
            return null;
          }
        }
      • 示例
        • PointMode改為points
          • 示例效果圖
        • PointMode改為polygon
          PointMode改為polygon,相鄰點互相連接
          • 示例效果
        • PointMode改為lines。
          PointMode為lines時,兩個點相互連接,也就是說第一個和第二個點連接,第三個跟第四個連接,如果最后只有一個點就舍棄不連接了,在我們的例子中有7個點,所以圖中只有三條連線。
          • 示例效果

    • 繪制圓rawCircle
      參數分別為:圓心的坐標、半徑和paint即可。圓形是否填充或描邊(或兩者)由Paint.style控制。
      • 系統方法
        void drawCircle(Offset c, double radius, Paint paint)
      • 使用方法
        class MyPainter extends CustomPainter {
          
          ///[定義畫筆]
          Paint _paint = Paint()
          ..color = Colors.blueAccent //畫筆顏色
          ..strokeCap = StrokeCap.round//畫筆筆觸類型
          ..isAntiAlias = true //是否啟動抗鋸齒
          ..style = PaintingStyle.stroke //繪畫風格,默認為填充
          ..strokeWidth = 5.0; //畫筆的寬度
        
          @override
          void paint(Canvas canvas, Size size) {
            //繪制圓 參數(圓心,半徑,畫筆)
            canvas.drawCircle(Offset(140,80), 80,_paint..color = Colors.green);
          }
        
          @override
          bool shouldRepaint(CustomPainter oldDelegate) {
            return null;
          }
        }
      • 示例 
        • PaintStyle.stroke 不填充
          ..style = PaintingStyle.stroke //繪畫風格,默認為不填充
          • 示例效果  
        • PaintStyle.stroke 填充

          將畫筆Paint的style改成了stroke,然后我們將畫筆style改成fill (填充) ,fill也是畫筆的style的默認值


          • 示例效果  
    • 繪制橢圓drawOval
      繪制一個軸對稱的橢圓形,參數為一個矩形和畫筆paint.
      • 系統方法
        void drawOval(Rect rect, Paint paint)
      • 使用方法

        class MyPainter extends CustomPainter {
          
          ///[定義畫筆]
          Paint _paint = Paint()
          ..color = Colors.blueAccent //畫筆顏色
          ..strokeCap = StrokeCap.round//畫筆筆觸類型
          ..isAntiAlias = true //是否啟動抗鋸齒
          ..style = PaintingStyle.fill //繪畫風格,默認為填充
          ..strokeWidth = 5.0; //畫筆的寬度
        
          @override
          void paint(Canvas canvas, Size size) {
            //使用左上和右下角坐標來確定矩形的大小和位置,橢圓是在這個矩形之中內切的
            Rect rect = Rect.fromPoints(Offset(100.0, 40.0), Offset(220.0, 100.0));
            canvas.drawOval(rect, _paint..color=Colors.green);
          }

        }

      • 示例
        Rect也有多種構建方式

        • fromLTWH(double left, double top, double width, double height)
          使用矩形左邊的X坐標、矩形頂部的Y坐標矩形的寬高來確定矩形的大小和位置
          • 使用方法
              @override
              void paint(Canvas canvas, Size size) {
                //使用矩形左邊的X坐標、矩形頂部的Y坐標矩形的寬高來確定矩形的大小和位置
                Rect rect = Rect.fromLTWH(100, 20, 100, 60);
                canvas.drawOval(rect, _paint..color=Colors.green);
              }
          • 示例效果
        • fromLTRB(double left, double top, double right, double bottom)
          使用矩形左邊的X坐標、矩形頂部的Y坐標、矩形右邊的X坐標、矩形底部的Y坐標來確定矩形的大小和位置
          • 使用方法
             @override
              void paint(Canvas canvas, Size size) {
                //使用矩形左邊的X坐標、矩形頂部的Y坐標、矩形右邊的X坐標、矩形底部的Y坐標來確定矩形的大小和位置
                Rect rect = Rect.fromLTRB(30, 30, 160, 140);
                canvas.drawOval(rect, _paint..color=Colors.green);
              }
          • 示例效果
        • fromCircle({ Offset center, double radius })
          使用圓的圓心點坐標和半徑和確定外切矩形的大小和位置
          • 使用方法
             @override
              void paint(Canvas canvas, Size size) {
                //使用圓的圓心點坐標和半徑和確定外切矩形的大小和位置
                Rect rect = Rect.fromCircle(center: Offset(100, 40),radius: 60);
                canvas.drawOval(rect, _paint..color=Colors.green);
              }
          • 示例效果
        • fromPoints(Offset a, Offset b)
          使用左上和右下角坐標來確定矩形的大小和位置

          • 使用方法
             @override
              void paint(Canvas canvas, Size size) {
                //使用左上和右下角坐標來確定矩形的大小和位置,橢圓是在這個矩形之中內切的
                Rect rect = Rect.fromPoints(Offset(100.0, 40.0), Offset(220.0, 100.0));
                canvas.drawOval(rect, _paint..color=Colors.green);
              }
          • 示例效果

    • 繪制圓弧drawArc
      首先還是需要Rect來確認圓弧的位置,還需要開始的弧度、結束的弧度、是否使用中心點繪制(圓弧是否向中心閉合)、以及paint.
      • 系統方法
        void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint)
      • 使用方法

        class MyPainter extends CustomPainter {
          
          ///[定義畫筆]
          Paint _paint = Paint()
          ..color = Colors.blueAccent //畫筆顏色
          ..strokeCap = StrokeCap.round//畫筆筆觸類型
          ..isAntiAlias = true //是否啟動抗鋸齒
          ..style = PaintingStyle.fill //繪畫風格,默認為填充
          ..strokeWidth = 5.0; //畫筆的寬度
        
          @override
          void paint(Canvas canvas, Size size) {
            // Rect來確認圓弧的位置,還需要開始的弧度、結束的弧度、是否使用中心點繪制、以及paint弧度
            Rect rect = Rect.fromCircle(center: Offset(100, 40),radius:80);
            canvas.drawArc(rect,0.0,0.8,false,_paint);
          }
        
          @override
          bool shouldRepaint(CustomPainter oldDelegate) {
            return null;
          }
        }
      • 拓展

        • 弧度
          根據定義,一周的弧度數為2πr/r=2π,360°角=2π弧度,因此,1弧度約為57.3°,即57°17’44.806’’,1°為π/180弧度,近似值為0.01745弧度,周角為2π弧度,平角(即180°角)為π弧度,直角為π/2弧度。

        • 特殊弧度

          弧度
          0
          30° π/6
          45° π/4
          60° π/3
          90° π/2
          120° 2π/3
          180° π
          270° 3π/2
          360°
          • 使用方法
             @override
              void paint(Canvas canvas, Size size) {
                // Rect來確認圓弧的位置,還需要開始的弧度、結束的弧度、是否使用中心點繪制、以及paint弧度
                const PI = 3.1415926;
                Rect rect2 = Rect.fromCircle(center: Offset(200.0, 50.0), radius: 80.0);
                canvas.drawArc(rect2, 0.0, PI / 2, false, _paint);
              }
          • 示例效果

        • 圓弧向中心點閉合
          將useCenter改成true,圓弧向中心點閉合了
          • 使用方法
             @override
              void paint(Canvas canvas, Size size) {
                // Rect來確認圓弧的位置,還需要開始的弧度、結束的弧度、是否使用中心點繪制、以及paint弧度
                const PI = 3.1415926;
                Rect rect2 = Rect.fromCircle(center: Offset(100.0, 50.0), radius: 80.0);
                canvas.drawArc(rect2, 0.0, PI / 2, true, _paint);
              }
          • 示例效果
    • 繪制圓角矩形drawDRRect
      使用RRect確定矩形大小及弧度,使用paint來完成繪制。RRect構建起來也非常的方便,直接使用fromRectAndRadius即可
      • 系統方法
        RRect構建
        RRect.fromRectAndRadius(rect, radius)

        RRect確定矩形大小及弧度,使用paint來完成繪制

        void drawRRect(RRect rrect, Paint paint)
      • 使用方法
        class MyPainter extends CustomPainter {
          
          ///[定義畫筆]
          Paint _paint = Paint()
          ..color = Colors.blueAccent //畫筆顏色
          ..strokeCap = StrokeCap.round//畫筆筆觸類型
          ..isAntiAlias = true //是否啟動抗鋸齒
          ..style = PaintingStyle.stroke //繪畫風格,默認為填充
          ..strokeWidth = 5.0; //畫筆的寬度
        
          @override
          void paint(Canvas canvas, Size size) {
             //用Rect構建一個邊長50,中心點坐標為100,100的矩形
            Rect rect = Rect.fromCircle(center: Offset(140.0, 50.0), radius: 50.0);
            //根據上面的矩形,構建一個圓角矩形
            RRect rrect = RRect.fromRectAndRadius(rect, Radius.circular(20.0));
            canvas.drawRRect(rrect, _paint);
          }
        
          @override
          bool shouldRepaint(CustomPainter oldDelegate) {
            return null;
          }
        }
      • 示例
        • rect依然用來表示矩形的位置和大小,radius用來表示圓角的大小
          • 使用方法
              @override
              void paint(Canvas canvas, Size size) {
                 //用Rect構建一個邊長50,中心點坐標為100,100的矩形
                Rect rect = Rect.fromCircle(center: Offset(140.0, 50.0), radius: 50.0);
                //根據上面的矩形,構建一個圓角矩形
                RRect rrect = RRect.fromRectAndRadius(rect, Radius.circular(20.0));
                canvas.drawRRect(rrect, _paint);
              }
          • 示例效果
        • 將圓角的半徑設置為邊長(從20改成50)
          • 使用方法
              @override
              void paint(Canvas canvas, Size size) {
                 //用Rect構建一個邊長50,中心點坐標為100,100的矩形
                Rect rect = Rect.fromCircle(center: Offset(140.0, 50.0), radius: 50.0);
                //根據上面的矩形,構建一個圓角矩形
                RRect rrect = RRect.fromRectAndRadius(rect, Radius.circular(50.0));
                canvas.drawRRect(rrect, _paint);
              }
          • 示例效果

    • 繪制雙圓角矩形drawRRect
      和drawRRect類似,使用RRect確定內部、外部矩形大小及弧度,使用paint來完成繪制。
      • 系統方法
        void drawDRRect(RRect outer, RRect inner, Paint paint)
      • 使用方法
        使用Rect.fromCircle來創建Rect,使用RRect.fromRectAndRadius來創建RRect
        class MyPainter extends CustomPainter {
          
          ///[定義畫筆]
          Paint _paint = Paint()
          ..color = Colors.blueAccent //畫筆顏色
          ..strokeCap = StrokeCap.round//畫筆筆觸類型
          ..isAntiAlias = true //是否啟動抗鋸齒
          ..style = PaintingStyle.stroke //繪畫風格,默認為填充
          ..strokeWidth = 5.0; //畫筆的寬度
        
          @override
          void paint(Canvas canvas, Size size) {
              
           //繪制兩個矩形
            Rect rect1 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 60.0);
            Rect rect2 = Rect.fromCircle(center: Offset(100.0, 100.0), radius: 40.0);
        
            //分別繪制外部圓角矩形和內部的圓角矩形
            RRect outer = RRect.fromRectAndRadius(rect1, Radius.circular(10.0));
            RRect inner = RRect.fromRectAndRadius(rect2, Radius.circular(10.0));
            canvas.drawDRRect(outer, inner, _paint);
          }
        
          @override
          bool shouldRepaint(CustomPainter oldDelegate) {
            return null;
          }
        }
      • 示例
        • 示例效果
        • 嘗試調整角度的度數大小
          • 使用方法
              @override
              void paint(Canvas canvas, Size size) {
                  
               //繪制兩個矩形
                Rect rect1 = Rect.fromCircle(center: Offset(150.0, 40.0), radius: 60.0);
                Rect rect2 = Rect.fromCircle(center: Offset(150.0, 40.0), radius: 40.0);
            
                //分別繪制外部圓角矩形和內部的圓角矩形
                RRect outer = RRect.fromRectAndRadius(rect1, Radius.circular(30.0));
                RRect inner = RRect.fromRectAndRadius(rect2, Radius.circular(10.0));
                canvas.drawDRRect(outer, inner, _paint);
              }
          • 示例效果

    • 繪制路徑drawPath
      繪制路徑,首先需要一個要繪制的路徑path,然后就是這個paint了。

      Path的常用方法:

      方法名            作用
      moveTo    將路徑起始點移動到指定的位置
      relativeMoveTo    相對於當前位置移動到
      lineTo    從當前位置連接指定點
      relativeLineTo    相對當前位置連接到
      arcTo    曲線
      conicTo    貝塞爾曲線
      add**    添加其他圖形,如addArc,在路徑是添加圓弧
      contains    路徑上是否包括某點
      transfor    給路徑做matrix4變換
      combine    結合兩個路徑
      close    關閉路徑,連接路徑的起始點
      reset    重置路徑,恢復到默認狀態
      • 系統方法

        void drawPath(Path path, Paint paint)
      • 使用方法

        class MyPainter extends CustomPainter {
          
          ///[定義畫筆]
          Paint _paint = Paint()
          ..color = Colors.blueAccent //畫筆顏色
          ..strokeCap = StrokeCap.round//畫筆筆觸類型
          ..isAntiAlias = true //是否啟動抗鋸齒
          ..style = PaintingStyle.stroke //繪畫風格,默認為填充
          ..strokeWidth = 5.0; //畫筆的寬度
        
          @override
          void paint(Canvas canvas, Size size) {
              
           //新建了一個path,然后將路徑起始點移動到坐標(100,100)的位置
            Path path = new Path()..moveTo(100.0, 100.0);
            path.lineTo(200.0, 200.0);
            canvas.drawPath(path, _paint);
          }
        
          @override
          bool shouldRepaint(CustomPainter oldDelegate) {
            return null;
          }
        }
        • 示例

          • 繪制直線  
            首先新建了一個path,然后將路徑起始點移動到坐標(100,100)的位置,
            然后從這個位置連接(200,200)的點.

            • 使用方法

               @override
                void paint(Canvas canvas, Size size) {
                    
                 //新建了一個path,然后將路徑起始點移動到坐標(100,100)的位置
                  Path path = new Path()..moveTo(100.0, 100.0);
                  path.lineTo(200.0, 200.0);
                  canvas.drawPath(path, _paint);
                }
            • 效果圖

          • 繪制多個路徑
            • 使用方法
               @override
                void paint(Canvas canvas, Size size) {
                    
                 Path path = new Path()..moveTo(100.0, 100.0);
              
                  path.lineTo(200.0, 200.0);
                  path.lineTo(100.0, 300.0);
                  path.lineTo(150.0, 350.0);
                  path.lineTo(150.0, 500.0);
                  
                  canvas.drawPath(path, _paint);
                }
            • 示例效果

參看:https://www.cnblogs.com/Free-Thinker/p/10218829.html

         


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM