简体   繁体   English

Flutter 自定义 Painter 绘图滞后

[英]Flutter Custom Painter drawing is laggy

I am trying to code a drawing app, in which users can choose different pen color and draw colorful drawings .我正在尝试编写一个绘图应用程序,用户可以在其中选择不同的笔颜色并绘制彩色绘图 I have created a class PointsGroup which stores list of offsets and associated color.我创建了一个 class PointsGroup 存储偏移列表和相关颜色。 In GestureDetector's onPanUpdate, the PointsGroup is appended to list of PointsGroup and passed to SignaturePainter.在 GestureDetector 的 onPanUpdate 中,PointsGroup 被附加到 PointsGroup 列表并传递给 SignaturePainter。

But the drawing is bit laggy, it is not drawn as soon as pen moves.但是画的有点慢,笔一动就画不出来。

You can see the video https://free.hubcap.video/v/LtOqoEj9H0dY9F9xC_jSst9HT3tSOJlTi可以看视频https://free.hubcap.video/v/LtOqoEj9H0dY9F9xC_jSst9HT3tSOJlTi

    import 'package:flutter/material.dart';

List<Color> colorList = [
  Colors.indigo,
  Colors.blue,
  Colors.green,
  Colors.yellow,
  Colors.orange,
  Colors.red
];

void main() => runApp(MaterialApp(
      home: HomePage(),
      debugShowCheckedModeBanner: false,
    ));

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<Offset> _points = <Offset>[];
  List<Offset> _setPoints = <Offset>[];
  List<PointsGroup> _ptsGroupList = <PointsGroup>[];
  int startIndex;
  int endIndex;

  @override
  void initState() {
    ColorChoser.penColor = Colors.black;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: <Widget>[
          GestureDetector(
            onPanStart: (details) {
              setState(() {
                _points.clear();
                startIndex = _ptsGroupList.length;
                ColorChoser.showColorSelector = false;
              });
            },
            onPanUpdate: (DragUpdateDetails details) {
              setState(() {
                RenderBox object = context.findRenderObject();
                Offset _localPosition =
                    object.globalToLocal(details.globalPosition);
                _points = new List.from(_points)..add(_localPosition);
                _setPoints = new List.from(_points);
                _ptsGroupList.add(new PointsGroup(
                    setPoints: _setPoints, setColor: ColorChoser.penColor));
              });
            },
            onPanEnd: (DragEndDetails details) {
              setState(() {
                _points.add(null);
                ColorChoser.showColorSelector = true;
                endIndex = _ptsGroupList.length;
                if (startIndex < endIndex) {
                  _ptsGroupList.replaceRange(
                      startIndex, endIndex - 1, [_ptsGroupList.removeLast()]);
                }
              });
            },
            child: CustomPaint(
              painter: SignaturePainter(grpPointsList: _ptsGroupList),
              size: Size.infinite,
            ),
          ),
          ColorChoser(),
        ],
      ),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.undo),
          onPressed: () {
            setState(() {
              if (_ptsGroupList.length > 0) {
                _ptsGroupList.removeLast();
              }
            });
          }),
    );
  }
}

class ColorChoser extends StatefulWidget {
  const ColorChoser({
    Key key,
  }) : super(key: key);

  static Color backgroundColor = Colors.white;
  static Color penColor = Colors.blue;
  static bool showColorSelector = true;

  @override
  _ColorChoserState createState() => _ColorChoserState();
}

class _ColorChoserState extends State<ColorChoser> {
  @override
  Widget build(BuildContext context) {
    return Visibility(
      visible: ColorChoser.showColorSelector,
      child: Positioned(
        bottom: 0,
        left: 0,
        width: MediaQuery.of(context).size.width,
        child: Container(
          height: 60,
          child: ListView.builder(
              scrollDirection: Axis.horizontal,
              itemCount: colorList.length,
              itemBuilder: (context, index) {
                return InkWell(
                  onTap: () {
                    setState(() {
                      ColorChoser.penColor = colorList[index];
                    });
                  },
                  child: Padding(
                    padding: const EdgeInsets.symmetric(
                        horizontal: 4.0, vertical: 5.0),
                    child: Container(
                      color: colorList[index],
                      // height: 30,
                      width: 45,
                    ),
                  ),
                );
              }),
        ),
      ),
    );
  }
}

class SignaturePainter extends CustomPainter {
  List<Offset> points;
  List<PointsGroup> grpPointsList = <PointsGroup>[];
  var paintObj;

  SignaturePainter({
    this.grpPointsList = const [],
  });

  @override
  void paint(Canvas canvas, Size size) {
    for (PointsGroup pts in grpPointsList) {
      points = pts.setPoints;
      paintObj = Paint()
        ..color = pts.setColor
        ..strokeCap = StrokeCap.round
        ..strokeWidth = 5.0;

      for (int i = 0; i < points.length - 1; i++) {
        if (points[i] != null && points[i + 1] != null) {
          canvas.drawLine(points[i], points[i + 1], paintObj);
        }
      }
    }
  }

  @override
  bool shouldRepaint(SignaturePainter oldDelegate) =>
      oldDelegate.points != points;
}

class PointsGroup {
  List<Offset> setPoints = <Offset>[];
  Color setColor;
  PointsGroup({this.setPoints, this.setColor});
}

Also the drawing is not shown for the very first draw.第一次抽奖时也没有显示该图。 As soon as pen is lifted it starts showing.笔一抬起,它就开始显示。

PS If there is any alternate way is to achieve the desired multi-colored drawing, it will be okay. PS如果有任何替代方法可以实现所需的多色绘图,那就没问题了。

You are clearing all the points whenever onPanStart is triggered (when the user places their finger on the screen).每当触发 onPanStart 时(当用户将手指放在屏幕上时),您都会清除所有点。 If you remove _points.clear() from onPanStart: (details) {} you will retain all the points that the user draws.如果您从onPanStart: (details) {}中删除_points.clear() ,您将保留用户绘制的所有点。

The application begins to lag and framerate is impacted after many points are drawn.绘制许多点后,应用程序开始滞后并且帧速率受到影响。 You'll notice this when the user has drawn a decent amount on the canvas.当用户在 canvas 上提取了可观的金额时,您会注意到这一点。 To prevent lag from occurring, one strategy is to reduce the number of points being drawn.为了防止出现滞后,一种策略是减少绘制的点数。 You can halve the number of points and still give the user the autonomy to draw what they desire by doing this:您可以将点数减半,但仍然可以通过这样做让用户自主绘制他们想要的东西:

final int THRESHOLD = 2;
if (totalPoints % THRESHOLD == 0){
 _points = new List.from(_points)..add(_localPosition);
}

totalPoints is a counter you increment by one in onPanUpdate: (details) {} totalPoints是您在onPanUpdate: (details) {}

Another technique is to wrap the subclass widget that extends CustomPainter, in this case CustomPaint, with a RepaintBoundary widget https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.html .另一种技术是使用 RepaintBoundary 小部件https://api.flutter.dev/flutter/widgets/RepaintBoundary-class.ZFC35FDC70D5FC69D23EZC.3222C35FDC70D5FC69D23EZ8 This widget will ensure that only the regions of the canvas where painting occurs is redrawn when needed.此小部件将确保仅在需要时重绘 canvas 中发生绘画的区域。 By limiting refresh rendering to one widget, you will speed up the process and deliver better results.通过将刷新渲染限制为一个小部件,您将加快流程并提供更好的结果。

RepaintBoundary(
  child: CustomPaint(
    isComplex: true,
    willChange: false,
    painter: Painter(
      points: _points,
    ),
  ),
),

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM