Flutter 提高 CustomPainter 动画性能

Jav*_*d98 7 profiling custom-painting flutter flutter-animation

我需要一个加载小部件,将移动的正弦和余弦函数绘制到画布中。我使用 CustomPaint 小部件和 CustomPainter 对其进行编码,没有任何问题,但是当我分析它时,我发现它的运行速度约为 49 fps,而不是 60 fps。UI 线程运行良好,每帧大约需要 6 毫秒,但光栅线程需要更长的时间。我尝试在画布上绘制更少的点(在 for 循环中使用 i=i+5 而不是 i++),但结果完全相同。

\n

\xc2\xbf有人可以建议我如何提高性能吗?下面是小部件代码,以及 Raster 线程在每一帧中执行的操作的 DevTools 屏幕截图,以防它有用。

\n
import \'dart:math\';\n\nimport \'package:flutter/material.dart\';\n\nclass LoadingChart extends StatefulWidget{\n  final Color color1;\n  final Color color2;\n  final double lineWidth;\n  final bool line;\n  final Size size;\n\n  const LoadingChart({\n    @required this.color1,\n    @required this.color2,\n    @required this.size,\n    @required this.lineWidth,\n    this.line = true,\n    Key key\n  }): super(key: key);\n\n  @override\n  State<StatefulWidget> createState() => _LoadingChartState();\n\n}\n\nclass _LoadingChartState extends State<LoadingChart>\n  with SingleTickerProviderStateMixin{\n  AnimationController _controller;\n\n\n  double randomHeight(Random random, double max){\n    return random.nextDouble()*max;\n  }\n\n  @override\n  void initState() {\n    _controller = AnimationController(vsync: this, duration: Duration(seconds: 1));\n    _controller.addListener(() {setState(() {});});\n    _controller.repeat();\n\n    super.initState();\n  }\n\n  @override\n  void dispose(){\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: widget.size.height,\n      width: widget.size.width,\n      child: CustomPaint(\n        painter: PathPainter(\n          color1: widget.color1,\n          color2: widget.color2,\n          value: _controller.value,\n          line: widget.line,\n        ),\n      )\n    );\n  }\n\n}\n\nclass PathPainter extends CustomPainter {\n  final Color color1;\n  final Color color2;\n  final double lineWidth;\n  final bool line;\n\n  final double value;\n\n  PathPainter({\n    @required this.value,\n    this.color1=Colors.red,\n    this.color2=Colors.green,\n    this.line = true,\n    this.lineWidth=4.0,\n  }): super();\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    final height = size.height;\n    final width = size.width;\n\n    Paint paint1 = Paint()\n      ..color = color1\n      ..style = PaintingStyle.stroke\n      ..strokeWidth = lineWidth;\n\n    Paint paint2 = Paint()\n      ..color = color2\n      ..style = PaintingStyle.stroke\n      ..strokeWidth = lineWidth;\n\n    Path path1 = Path();\n    Path path2 = Path();\n\n    /* If line is true, draw sin and cos functions, otherwise, just some points */\n    for (double i = 0; i < width; i=i+5){\n      double f = i*2*pi/width + 2*pi*value;\n      double g = i*2*pi/width - 2*pi*value;\n      if (i == 0){\n        path1.moveTo(0, height/2 + height/6*sin(f));\n        path2.moveTo(0, height/2 + height/6*cos(g));\n        continue;\n      }\n\n      path1.lineTo(i, height/2 + height/6*sin(f));\n      path2.lineTo(i, height/2 + height/6*cos(g));\n    }\n\n    /* Draw both lines */\n    canvas.drawPath(path1, paint1);\n    canvas.drawPath(path2, paint2);\n  }\n\n  @override\n  bool shouldRepaint(PathPainter oldDelegate) {\n   return oldDelegate.value != value || oldDelegate.color1 != color1\n     || oldDelegate.color2 != color2 || oldDelegate.line != line\n     || oldDelegate.lineWidth != lineWidth;\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在此输入图像描述\n在此输入图像描述

\n

PS:我正在配置文件模式下运行应用程序,因此这应该不是问题。我还想提一下,它是屏幕上唯一被重绘的小部件。\n非常感谢!

\n

Edw*_*nZN 8

CustomPainter 可以接收可听的,因此也许您可以使用那里的动画控制器在每次勾选时更新它

class _LoadingChartState extends State<LoadingChart>
    with SingleTickerProviderStateMixin{
  AnimationController _controller;


  double randomHeight(Random random, double max){
    return random.nextDouble()*max;
  }

  @override
  void initState() {
    _controller = AnimationController(vsync: this, duration: Duration(seconds: 1));
    //_controller.addListener(() {setState(() {});}); no need to setState
    _controller.repeat();

    super.initState();
  }

  @override
  void dispose(){
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
        height: widget.size.height,
        width: widget.size.width,
        child: CustomPaint(
          willChange: true, //this can help (Whether the raster cache should be told that this painting is likely)
          painter: PathPainter(
            color1: widget.color1,
            color2: widget.color2,
            line: widget.line,
            listenable: _controller //pass the controller as it is (An animationController extends a Listenable)
          ),
        )
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

在 PathPainter 中,您向构造函数提供可监听对象,并将其传递给接受名为 repaint 的可监听对象的 CustomPainter 构造函数

class PathPainter extends CustomPainter {
  final Animation listenable;
  final Color color1;
  final Color color2;
  final double lineWidth;
  final bool line;

  PathPainter({
    this.listenable,
    this.color1=Colors.red,
    this.color2=Colors.green,
    this.line = true,
    this.lineWidth=4.0,
  }): super(repaint: listenable); //don't forget calling the CustomPainter constructor with super

  @override
  void paint(Canvas canvas, Size size) {
    double value = listenable.value; // get its value here
    final height = size.height;
    final width = size.width;

    Paint paint1 = Paint()
      ..color = color1
      ..style = PaintingStyle.stroke
      ..strokeWidth = lineWidth;

    Paint paint2 = Paint()
      ..color = color2
      ..style = PaintingStyle.stroke
      ..strokeWidth = lineWidth;

    Path path1 = Path();
    Path path2 = Path();

    /* If line is true, draw sin and cos functions, otherwise, just some points */
    for (double i = 0; i < width; i=i+5){
      double f = i*2*pi/width + 2*pi*value;
      double g = i*2*pi/width - 2*pi*value;
      if (i == 0){
        path1.moveTo(0, height/2 + height/6*sin(f));
        path2.moveTo(0, height/2 + height/6*cos(g));
        continue;
      }

      path1.lineTo(i, height/2 + height/6*sin(f));
      path2.lineTo(i, height/2 + height/6*cos(g));
    }

    /* Draw both lines */
    canvas.drawPath(path1, paint1);
    canvas.drawPath(path2, paint2);
  }

  @override
  bool shouldRepaint(PathPainter oldDelegate) {
    //delete the oldDelegate.value, it doesn't exists anymore
    return oldDelegate.color1 != color1
        || oldDelegate.color2 != color2 || oldDelegate.line != line
        || oldDelegate.lineWidth != lineWidth;
  }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我处于调试模式,因此我希望您在配置文件模式下获得更好的性能