8

我已经尝试在画布上使用Canvas.clipPathwithGestureDetector来像橡皮擦一样使用CustomPaint里面的Containerwith imageDecorationset,所以我想也许有另一种解决方法,通过使用Canvas.drawPathalong 设置

 final Paint _eraserPaint = Paint()
    ..color = Colors.transparent
    ..blendMode = BlendMode.clear
    ..strokeWidth = 8
    ..style = PaintingStyle.stroke
    ..isAntiAlias = true;

但它会画黑线而不是擦除

知道如何解决这个问题吗?

谢谢

4

4 回答 4

4

关键是saveLayer在绘制任何可能需要擦除的内容之前调用。完成后(从而创建一个新图层供您使用),然后您可以使用任何Color填充或绘制BlendMode.clear擦除。最后,调用restore将新层“合并”到其他现有层中。

例如,让我们画一个红色正方形并从中减去一个圆圈:

void paint(Canvas canvas, Size size) {
  canvas.saveLayer(Rect.largest, Paint());
  canvas.drawRect(Rect.fromLTWH(0, 0, 80, 80), Paint()..color = Colors.red);
  canvas.drawCircle(Offset(40, 40), 40, Paint()..blendMode = BlendMode.clear);
  canvas.restore();
}

样本结果:

一个正方形,中间切一个圆

于 2021-04-30T16:29:01.557 回答
1

希望这段代码可以帮助你!

class DrawingPainter extends CustomPainter {

  List<DrawingPoints> pointsList;
  List<Offset> offsetPoints = List();
  
  DrawingPainter({
    this.pointsList,
  });

  @override
  void paint(Canvas canvas, Size size) {
    canvas.saveLayer(Rect.fromLTWH(0, 0, size.width, size.height), Paint());
    for (int i = 0; i < pointsList.length - 1; i++) {
      if (pointsList[i] != null && pointsList[i + 1] != null) {
        canvas.drawLine(pointsList[i].points, pointsList[i + 1].points, pointsList[i].paint);
        canvas.drawCircle(pointsList[i].points, pointsList[i].paint.strokeWidth/2, pointsList[i].paint);
      }
    }
    canvas.restore();
  }

  @override
  bool shouldRepaint(DrawingPainter oldDelegate) => true;
}

class DrawingPoints {
  Paint paint;
  Offset points;
  DrawingPoints({this.points, this.paint});
}

您需要saveLayer,然后恢复以保存Paint

也许您需要将此代码添加到 Statefull 小部件。

void changeBrush(bool isErease){
    setState(() {
      if ( isErease ){
        paint = Paint();
        paint.blendMode = BlendMode.clear;
        paint.color = Colors.white;
        paint.strokeWidth = strokeWidth;
      }else{
        paint = Paint();
        paint.isAntiAlias = true;
        paint.color = selectedColor.withOpacity(opacity);
        paint.strokeWidth = strokeWidth;
      }
    });
  }
于 2020-08-24T03:19:36.700 回答
0

将您的自定义绘画小部件包装成不透明度为 0.99 的不透明度小部件:

        Opacity(
          opacity: .99,
          child: CustomPaint(
            size: const Size(double.infinity, 100),
            painter: MyPainter(),
          ),
        ),

我不知道为什么,但这可以解决问题,而您的画家课程没有任何改变。

于 2021-12-22T11:08:59.670 回答
0

这段代码可以帮助你...

import 'dart:convert';
    import 'dart:io';
    import 'dart:typed_data';
    import 'dart:ui' as ui;
    
    import 'package:animated_floatactionbuttons/animated_floatactionbuttons.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    import 'package:flutter/services.dart';
    import 'package:image_gallery_saver/image_gallery_saver.dart';
    import 'package:image_picker/image_picker.dart';
    import 'package:permission_handler/permission_handler.dart';
    
    
    var appbarcolor = Colors.blue;
    class CanvasPainting_test extends StatefulWidget {
    
      @override
      _CanvasPainting_testState createState() => _CanvasPainting_testState();
    }
    
    class _CanvasPainting_testState extends State<CanvasPainting_test> {
      GlobalKey globalKey = GlobalKey();
    
      List<TouchPoints> points = List();
      double opacity = 1.0;
      StrokeCap strokeType = StrokeCap.round;
      double strokeWidth = 3.0;
      double strokeWidthforEraser = 3.0;
      Color selectedColor;
    
      Future<void> _pickStroke() async {
        //Shows AlertDialog
        return showDialog<void>(
          context: context,
    
          //Dismiss alert dialog when set true
          barrierDismissible: true, // user must tap button!
          builder: (BuildContext context) {
            //Clips its child in a oval shape
            return ClipOval(
              child: AlertDialog(
                //Creates three buttons to pick stroke value.
                actions: <Widget>[
                  //Resetting to default stroke value
                  FlatButton(
                    child: Icon(
                      Icons.clear,
                    ),
                    onPressed: () {
                      strokeWidth = 3.0;
                      Navigator.of(context).pop();
                    },
                  ),
                  FlatButton(
                    child: Icon(
                      Icons.brush,
                      size: 24,
                    ),
                    onPressed: () {
                      strokeWidth = 10.0;
                      Navigator.of(context).pop();
                    },
                  ),
                  FlatButton(
                    child: Icon(
                      Icons.brush,
                      size: 40,
                    ),
                    onPressed: () {
                      strokeWidth = 30.0;
                      Navigator.of(context).pop();
                    },
                  ),
                  FlatButton(
                    child: Icon(
                      Icons.brush,
                      size: 60,
                    ),
                    onPressed: () {
                      strokeWidth = 50.0;
                      Navigator.of(context).pop();
                    },
                  ),
                ],
              ),
            );
          },
        );
      }
    
      Future<void> _opacity() async {
        //Shows AlertDialog
        return showDialog<void>(
          context: context,
    
          //Dismiss alert dialog when set true
          barrierDismissible: true,
    
          builder: (BuildContext context) {
            //Clips its child in a oval shape
            return ClipOval(
              child: AlertDialog(
                //Creates three buttons to pick opacity value.
                actions: <Widget>[
                  FlatButton(
                    child: Icon(
                      Icons.opacity,
                      size: 24,
                    ),
                    onPressed: () {
                      //most transparent
                      opacity = 0.1;
                      Navigator.of(context).pop();
                    },
                  ),
                  FlatButton(
                    child: Icon(
                      Icons.opacity,
                      size: 40,
                    ),
                    onPressed: () {
                      opacity = 0.5;
                      Navigator.of(context).pop();
                    },
                  ),
                  FlatButton(
                    child: Icon(
                      Icons.opacity,
                      size: 60,
                    ),
                    onPressed: () {
                      //not transparent at all.
                      opacity = 1.0;
                      Navigator.of(context).pop();
                    },
                  ),
                ],
              ),
            );
          },
        );
      }
    
      Future<void> _pickStrokeforEraser() async {
        //Shows AlertDialog
        return showDialog<void>(
          context: context,
    
          //Dismiss alert dialog when set true
          barrierDismissible: true, // user must tap button!
          builder: (BuildContext context) {
            //Clips its child in a oval shape
            return ClipOval(
              child: AlertDialog(
                //Creates three buttons to pick stroke value.
                actions: <Widget>[
                  //Resetting to default stroke value
                  FlatButton(
                    child: Icon(
                      Icons.clear,
                    ),
                    onPressed: () {
                      strokeWidthforEraser = 3.0;
                      Navigator.of(context).pop();
                    },
                  ),
                  FlatButton(
                    child: Icon(
                      Icons.brush,
                      size: 24,
                    ),
                    onPressed: () {
                      strokeWidthforEraser = 10.0;
                      Navigator.of(context).pop();
                    },
                  ),
                  FlatButton(
                    child: Icon(
                      Icons.brush,
                      size: 40,
                    ),
                    onPressed: () {
                      strokeWidthforEraser = 30.0;
                      Navigator.of(context).pop();
                    },
                  ),
                  FlatButton(
                    child: Icon(
                      Icons.brush,
                      size: 60,
                    ),
                    onPressed: () {
                      strokeWidthforEraser = 50.0;
                      Navigator.of(context).pop();
                    },
                  ),
                ],
              ),
            );
          },
        );
      }
    
      Future<void> _save() async {
        RenderRepaintBoundary boundary =
        globalKey.currentContext.findRenderObject();
        ui.Image image = await boundary.toImage();
        ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
        Uint8List pngBytes = byteData.buffer.asUint8List();
    
        //Request permissions if not already granted
        if (!(await Permission.storage.status.isGranted))
          await Permission.storage.request();
    
        final result = await ImageGallerySaver.saveImage(
            Uint8List.fromList(pngBytes),
            quality: 60,
            name: "canvas_image");
        print(result);
      }
      String erase = 'yes';
      List<Widget> fabOption() {
        return <Widget>[
          FloatingActionButton(
            backgroundColor: appbarcolor,
            heroTag: "camera",
            child: Icon(Icons.camera),
            tooltip: 'camera',
            onPressed: () {
              //min: 0, max: 50
              setState(() {
                erase = 'yes';
                this._showDialog();
                // _save();
              });
            },
          ),
          FloatingActionButton(
            backgroundColor: appbarcolor,
            heroTag: "paint_save",
            child: Icon(Icons.file_download),
            tooltip: 'Save',
            onPressed: () {
              //min: 0, max: 50
              setState(() {
                erase = 'yes';
                _save();
              });
            },
          ),
          FloatingActionButton(
            backgroundColor: appbarcolor,
            heroTag: "paint_stroke",
            child: Icon(Icons.brush),
            tooltip: 'Stroke',
            onPressed: () {
              //min: 0, max: 50
              setState(() {
                erase = 'yes';
                _pickStroke();
              });
            },
          ),
          // FloatingActionButton(
          //   heroTag: "paint_opacity",
          //   child: Icon(Icons.opacity),
          //   tooltip: 'Opacity',
          //   onPressed: () {
          //     //min:0, max:1
          //     setState(() {
          //       _opacity();
          //     });
          //   },
          // ),
          FloatingActionButton(
            backgroundColor: appbarcolor,
            heroTag: "Erase",
            child: Icon(Icons.ac_unit),
            tooltip: 'Erase',
            onPressed: () {
              //min: 0, max: 50
              setState(() {
                // _save();
                // selectedColor = Colors.transparent;
                // print(Platform.isAndroid);
                erase = 'no';
                _pickStrokeforEraser();
              });
            },
          ),
          FloatingActionButton(
              backgroundColor: appbarcolor,
              heroTag: "Clear All",
              child: Icon(Icons.clear),
              tooltip: "Clear All",
              onPressed: () {
                setState(() {
                  erase = 'yes';
                  points.clear();
                });
              }),
          FloatingActionButton(
            backgroundColor: Colors.white,
            heroTag: "color_red",
            child: colorMenuItem(Colors.red),
            tooltip: 'Color',
            onPressed: () {
              setState(() {
                selectedColor = Colors.red;
              });
            },
          ),
          FloatingActionButton(
            backgroundColor: Colors.white,
            heroTag: "color_green",
            child: colorMenuItem(Colors.green),
            tooltip: 'Color',
            onPressed: () {
              setState(() {
                erase = 'yes';
                selectedColor = Colors.green;
              });
            },
          ),
          FloatingActionButton(
            backgroundColor: Colors.white,
            heroTag: "color_pink",
            child: colorMenuItem(Colors.pink),
            tooltip: 'Color',
            onPressed: () {
              setState(() {
                selectedColor = Colors.pink;
              });
            },
          ),
          FloatingActionButton(
            backgroundColor: Colors.white,
            heroTag: "color_blue",
            child: colorMenuItem(Colors.blue),
            tooltip: 'Color',
            onPressed: () {
              setState(() {
                erase = 'yes';
                selectedColor = Colors.blue;
              });
            },
          ),
        ];
      }
    
    
      void _showDialog() {
        // flutter defined function
        showDialog(
          context: context,
          builder: (BuildContext context) {
            // return object of type Dialog
            return AlertDialog(
    //          title: new Text("Alert Dialog title"),
              content: Row(
                // mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  RaisedButton(
                    onPressed: getImageCamera,
                    child: Text('From Camera'),
                  ),
                  SizedBox(
                    width: 5,
                  ),
                  RaisedButton(
                    onPressed: getImageGallery,
                    child: Text('From Gallery'),
                  )
                ],
              ),
            );
          },
        );
      }
      File _image;
      Future getImageCamera() async {
        var image = await ImagePicker.pickImage(source: ImageSource.camera);
    
        print(image);
    
        if (image != null) {
          setState(() {
            _image = image;
          });
          Navigator.of(context, rootNavigator: true).pop('dialog');
        }
      }
    
      Future getImageGallery() async {
        var image = await ImagePicker.pickImage(source: ImageSource.gallery);
    
        print(image);
    
        if (image != null) {
          setState(() {
            _image = image;
            print(_image);
          });
          Navigator.of(context, rootNavigator: true).pop('dialog');
        }
      }
      /*-------------------------------------*/
    
    
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('paint on image and erase'),backgroundColor: Colors.blueGrey
            // leading: IconButton(
            //   icon: Icon(Icons.arrow_back_ios),onPressed: (){
            //   Navigator.pop(context);
            // },),
          ),
          body: GestureDetector(
            onPanUpdate: (details) {
              setState(() {
                RenderBox renderBox = context.findRenderObject();
                erase!='no'?   points.add(TouchPoints(
                    points: renderBox.globalToLocal(details.globalPosition),
                    paint: Paint()
                      ..strokeCap = strokeType
                      ..isAntiAlias = true
                      ..color = selectedColor.withOpacity(opacity)
                      ..strokeWidth = strokeWidth))
    
                    : points.add(TouchPoints(
                    points:  renderBox.globalToLocal(details.globalPosition),
                    paint: Paint()
                      ..color = Colors.transparent
                      ..blendMode = BlendMode.clear
                      ..strokeWidth = strokeWidthforEraser
                      ..style = PaintingStyle.stroke
                      ..isAntiAlias = true
                ));
              });
            },
            onPanStart: (details) {
              setState(() {
                RenderBox renderBox = context.findRenderObject();
                erase!='no'?   points.add(TouchPoints(
                    points: renderBox.globalToLocal(details.globalPosition),
                    paint: Paint()
                      ..strokeCap = strokeType
                      ..isAntiAlias = true
                      ..color = selectedColor.withOpacity(opacity)
                      ..strokeWidth = strokeWidth))
    
                    : points.add(TouchPoints(
                    points:  renderBox.globalToLocal(details.globalPosition),
                    paint: Paint()
                      ..color = Colors.transparent
                      ..blendMode = BlendMode.clear
                      ..strokeWidth = strokeWidthforEraser
                      ..style = PaintingStyle.stroke
                      ..isAntiAlias = true
                ));
    
    
              });
            },
            onPanEnd: (details) {
              setState(() {
                points.add(null);
              });
            },
            child: RepaintBoundary(
              key: globalKey,
              child: Stack(
                children: <Widget>[
                  Center(
                    child: _image == null
                        ? Image.asset(
                      "assets/images/helo.jfif",
                    )
                        : Image.file(_image),
                  ),
                  CustomPaint(
                    size: Size.infinite,
                    painter: MyPainter(
                      pointsList: points,
                    ),
                  ),
                ],
              ),
            ),
          ),
          floatingActionButton: AnimatedFloatingActionButton(
              fabButtons: fabOption(),
              colorStartAnimation: appbarcolor,
              colorEndAnimation: Colors.red[300],
              animatedIconData: AnimatedIcons.menu_close),
        );
      }
    
      Widget colorMenuItem(Color color) {
        return GestureDetector(
          onTap: () {
            setState(() {
              selectedColor = color;
            });
          },
          child: ClipOval(
            child: Container(
              padding: const EdgeInsets.only(bottom: 8.0),
              height: 36,
              width: 36,
              color: color,
            ),
          ),
        );
      }
    
    
    
    
    
    
    
    
    }
    
    class MyPainter extends CustomPainter {
      MyPainter({this.pointsList});
    
      //Keep track of the points tapped on the screen
      List<TouchPoints> pointsList;
      List<Offset> offsetPoints = List();
    
      //This is where we can draw on canvas.
      @override
      void paint(Canvas canvas, Size size) {
        canvas.saveLayer(Rect.fromLTWH(0, 0, size.width, size.height), Paint());
        for (int i = 0; i < pointsList.length - 1; i++) {
          if (pointsList[i] != null && pointsList[i + 1] != null) {
            canvas.drawLine(pointsList[i].points, pointsList[i + 1].points, pointsList[i].paint);
            canvas.drawCircle(pointsList[i].points, pointsList[i].paint.strokeWidth/2, pointsList[i].paint);
          }
        }
        canvas.restore();
      }
    
      //Called when CustomPainter is rebuilt.
      //Returning true because we want canvas to be rebuilt to reflect new changes.
      @override
      bool shouldRepaint(MyPainter oldDelegate) => true;
    }
    
    //Class to define a point touched at canvas
    class TouchPoints {
      Paint paint;
      Offset points;
      TouchPoints({this.points, this.paint});
    }
于 2020-11-20T12:37:01.920 回答