美文网首页Flutter
flutter之CustomPaint

flutter之CustomPaint

作者: RobinZhao | 来源:发表于2019-04-28 14:32 被阅读0次
效果展示

CustomPaint

 class CustomPaint extends SingleChildRenderObjectWidget

 const CustomPaint({
    Key key,
    this.painter,
    this.foregroundPainter,
    this.size = Size.zero,
    this.isComplex = false,
    this.willChange = false,
    Widget child,
  }) : assert(size != null),
       assert(isComplex != null),
       assert(willChange != null),
       super(key: key, child: child);

CustomPaint继承自SingleChildRenderObjectWidget,所以不能用setState来刷新!

其中:

  • painter就是我们的主绘制工具,它是一个CustomPainter,画笔角色;
  • foregroundPainter是用来绘制前景的工具;
  • size为画布大小,这个size会传递给Painter;
  • isComplex和willChange 是告诉Flutter你的CustomPaint是否复杂到需要使用cache相关的功能;
  • child属性我们一般不填,即使你是想要在你的CustomPaint上添加一些其他的布局,也不建议放在child属中性,因为你会发现你并不会得到你想要的结果。

所有的绘制都是发生在Painter里面的,绘制的代码写在我们的自定义CustomPainter中:

CustomPainter

class LinePainter extends CustomPainter {
  
  ///Flutter中负责View绘制的地方,使用传递来的canvas和size即可完成对目标View的绘制
  @override
  void paint(Canvas canvas, Size size) {
    // 绘制直线
    _drawLine(canvas);

    // 绘制点
    _drawPoint(canvas);

    // 绘制圆
    _drawCircle1(canvas);
    _drawCircle2(canvas);

    // 绘制椭圆drawOval
    _drawOval(canvas);

    //绘制圆弧drawArc
    _drawArc1(canvas);
    _drawArc2(canvas);

    // 绘制圆角矩形drawDRRect
    _drawRRect(canvas);

    // 绘制双圆角矩形drawDRRect
    _drawDRRect(canvas);
    _drawDRRect1(canvas);

    // 绘制路径drawPath
    _drawPath(canvas);

  }

  ///控制自定义View是否需要重绘的,返回false代表这个View在构建完成后不需要重绘。
  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

我们需要重写paint()和shouldRepaint()这两个方法,前者是绘制流程,后者是在刷新布局的时候告诉Flutter是否需要重绘。注意下paint方法中的size参数,就是我们在CustomPaint中定义的size属性,它包含了基本的画布大小信息。

真正地绘制则是通过canvas和Paint来实现的,我们将定义好了的Paint画笔传递给canvas.drawXXX()方法

Paint

Paint _paint = Paint()
  ..color = Colors.blueAccent // 画笔颜色
  ..strokeCap = StrokeCap.round // 画笔笔触类型
  ..isAntiAlias = true // 是否启动抗锯齿
  // ..blendMode = BlendMode.exclusion // 颜色混合模式
  ..style = PaintingStyle.stroke // 绘画风格,默认是填充
  // ..colorFilter = ColorFilter.mode(Colors.blueAccent,
  // BlendMode.exclusion) // 颜色渲染模式,一般是矩阵效果来改变的,但是flutter中只能使用颜色混合模式
  // ..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) // 模糊遮罩效果,flutter中只有这个
  ..filterQuality = FilterQuality.high
  ..strokeWidth = 5.0;

具体的一些实现

注意,Demo是以iPhone8 plus设备为例,未做全部机型适配

void _drawLine(Canvas canvas) {
  // 绘制直线
  Offset p1 = Offset(20, 20);
  Offset p2 = Offset(120, 20);
  canvas.drawLine(p1, p2, _paint);
}

void _drawPoint(Canvas canvas) {
  canvas.drawPoints(

      ///PointMode的枚举类型有三个,
      ///points(点), lines(线,隔点连接), polygon(线,相邻连接)
      PointMode.polygon,
      [
        Offset(20, 50),
        Offset(120, 50),
        Offset(80, 100),
        Offset(80, 150),
        Offset(60, 170),
        Offset(60, 100),
        Offset(20, 50),
      ],
      _paint);
}

void _drawCircle1(Canvas canvas) {
  // 绘制圆 参数(圆心,半径,画笔)
  canvas.drawCircle(Offset(150, 80), 20, _paint);
}

void _drawCircle2(Canvas canvas) {
  Paint paint = Paint()
    ..color = Colors.green
    ..strokeCap = StrokeCap.round
    ..isAntiAlias = true
    ..style = PaintingStyle.fill
    ..strokeWidth = 5;
  ;
  // 绘制圆 参数(圆心,半径,画笔)
  canvas.drawCircle(Offset(150, 140), 20, paint);
}

void _drawOval(Canvas canvas) {
  Paint paint = Paint()
    ..color = Colors.red
    ..strokeCap = StrokeCap.round
    ..isAntiAlias = true
    ..strokeWidth = 5
    ..style = PaintingStyle.stroke;

  /** 
    使用左上和右下角坐标来确定矩形的大小和位置
    fromPoints(Offset a, Offset b)
    
    使用圆的圆心点坐标和半径和确定外切矩形的大小和位置
    fromCircle({ Offset center, double radius })
    
    使用矩形左边的X坐标、矩形顶部的Y坐标、矩形右边的X坐标、矩形底部的Y坐标来确定矩形的大小和位置
    fromLTRB(double left, double top, double right, double bottom)

    使用矩形左边的X坐标、矩形顶部的Y坐标矩形的宽高来确定矩形的大小和位置
    fromLTWH(double left, double top, double width, double height)
 */
  Rect rect = Rect.fromPoints(Offset(200, 20), Offset(300, 60));
  canvas.drawRect(rect, paint);
  canvas.drawOval(rect, _paint);
}

void _drawArc1(Canvas canvas) {
  Paint paint = Paint()
    ..color = Colors.orange
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 5
    ..style = PaintingStyle.stroke
    ..isAntiAlias = true;

  Rect rect = Rect.fromLTWH(200, 80, 100, 40);
  canvas.drawRect(rect, paint);
  double startAngle = 0;
  double sweepAngle = PI / 2;
  bool useCenter = true;
  canvas.drawArc(rect, startAngle, sweepAngle, useCenter, _paint);
}

void _drawArc2(Canvas canvas) {
  Paint paint = Paint()
    ..color = Colors.purple
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 5
    ..style = PaintingStyle.stroke
    ..isAntiAlias = true;

  Rect rect = Rect.fromLTWH(320, 20, 80, 80);
  canvas.drawRect(rect, paint);
  double startAngle = 0;
  double sweepAngle = PI / 2;
  bool useCenter = true;
  canvas.drawArc(rect, startAngle, sweepAngle, useCenter, _paint);
}

void _drawRRect(Canvas canvas) {
  Rect rect = Rect.fromCircle(center: Offset(70, 250), radius: 50);
  Radius radius = Radius.circular(20);
  // Radius radius = Radius.circular(50); 改成50会变成一个圆
  RRect rrect = RRect.fromRectAndRadius(rect, radius);
  // RRect rrect = RRect.fromRectAndCorners(rect,topLeft: Radius.circular(20),bottomRight: Radius.circular(10));
  canvas.drawRRect(rrect, _paint);
}

void _drawDRRect(Canvas canvas) {
  Radius radius1 = Radius.circular(10);
  Rect rect1 = Rect.fromCircle(center: Offset(200, 250), radius: 50);
  RRect outer = RRect.fromRectAndRadius(rect1, radius1);
  // canvas.drawRRect(inner, _paint);
  Radius radius2 = Radius.circular(5);
  Rect rect2 = Rect.fromCircle(center: Offset(200, 250), radius: 20);
  RRect inner = RRect.fromRectAndRadius(rect2, radius2);
  // canvas.drawRRect(outer, _paint);
  canvas.drawDRRect(outer, inner, _paint); // 注意 当outer和inner位置相反时,绘制不出结果
}

void _drawDRRect1(Canvas canvas) {
  Paint paint1 = Paint()
    ..color = Color(0xffc8a556)
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 5
    ..style = PaintingStyle.stroke
    ..isAntiAlias = true;

  // Paint paint2 = Paint()
  // ..color = Color.fromRGBO(0, 0, 0, 1)
  // ..strokeCap = StrokeCap.round
  // ..strokeWidth = 5
  // ..style = PaintingStyle.fill
  // ..isAntiAlias = true
  // ;
  Radius radius1 = Radius.circular(50);
  Rect rect1 = Rect.fromCircle(center: Offset(330, 250), radius: 50);
  RRect outer = RRect.fromRectAndRadius(rect1, radius1);
  canvas.drawRRect(outer, paint1);
  Radius radius2 = Radius.circular(5);
  Rect rect2 = Rect.fromCircle(center: Offset(330, 250), radius: 20);
  RRect inner = RRect.fromRectAndRadius(rect2, radius2);
  canvas.drawRRect(inner, paint1);
}

void _drawPath(Canvas canvas) {
/**
    Path的常用方法:
    将路径起始点移动到指定的位置
    moveTo
    相对于当前位置移动到
    relativeMoveTo
    从当前位置连接指定点
    lineTo
    相对当前位置连接到
    relativeLineTo
    曲线
    arcTo
    贝塞尔曲线
    conicTo
    添加其他图形,如addArc,在路径是添加圆弧
    add**
    路径上是否包括某点
    contains
    给路径做matrix4变换
    transfor
    结合两个路径
    combine
    关闭路径,连接路径的起始点
    close
    重置路径,恢复到默认状态
    reset
 */

  Path path = Path()
  ..moveTo(20, 320);
  path.lineTo(50, 340);
  path.lineTo(70, 330);
  path.relativeLineTo(10, 20);
  canvas.drawPath(path, _paint);

  // 贝赛尔曲线
  Path path1 = Path()..moveTo(90, 330);
  Rect rect = Rect.fromLTWH(100, 330, 60, 60);
  // 如果“forceMoveTo”参数为false,则添加一条直线段和一条弧段。
  // 如果“forceMoveTo”参数为true,则启动一个新的子路径,其中包含一个弧段。
  // path1.arcTo(rect, startAngle, sweepAngle, forceMoveTo)
  path1.arcTo(rect, 0, PI/2, false);
  canvas.drawPath(path1, _paint);

  Path path2 = Path()..moveTo(150, 330);
  Rect rect2 = Rect.fromLTWH(150, 330, 60, 60);
  path2.arcTo(rect2, 0, PI/2, true);
  canvas.drawPath(path2, _paint);

  Path path3 = Path()..moveTo(250, 330);
  Rect rect3 = Rect.fromLTWH(250, 330, 60, 60);
  path3.arcTo(rect3, 0, 3.14 * 2, true); // PI *2/3.141592654 *2不会有绘制结果 
  canvas.drawPath(path3, _paint);
  // 使用控制点(x1,y1)和权重w,添加从当前点到给定点(x2,y2)的曲线段。 
  // 如果重量大于1,那么曲线就是双曲线; 如果重量等于1,那就是抛物线; 如果小于1,则为椭圆形。
  // path.conicTo(x1, y1, x2, y2, w)
}

相关文章

网友评论

    本文标题:flutter之CustomPaint

    本文链接:https://www.haomeiwen.com/subject/jpbdnqtx.html