2

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

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

import 'dart:math';

import 'package:flutter/material.dart';

class LoadingChart extends StatefulWidget{
  final Color color1;
  final Color color2;
  final double lineWidth;
  final bool line;
  final Size size;

  const LoadingChart({
    @required this.color1,
    @required this.color2,
    @required this.size,
    @required this.lineWidth,
    this.line = true,
    Key key
  }): super(key: key);

  @override
  State<StatefulWidget> createState() => _LoadingChartState();

}

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(() {});});
    _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(
        painter: PathPainter(
          color1: widget.color1,
          color2: widget.color2,
          value: _controller.value,
          line: widget.line,
        ),
      )
    );
  }

}

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

  final double value;

  PathPainter({
    @required this.value,
    this.color1=Colors.red,
    this.color2=Colors.green,
    this.line = true,
    this.lineWidth=4.0,
  }): super();

  @override
  void paint(Canvas canvas, Size size) {
    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) {
   return oldDelegate.value != value || oldDelegate.color1 != color1
     || oldDelegate.color2 != color2 || oldDelegate.line != line
     || oldDelegate.lineWidth != lineWidth;
  }
}

在此处输入图像描述 在此处输入图像描述

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

4

1 回答 1

6

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)
          ),
        )
    );
  }
}

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

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;
  }
}

在此处输入图像描述

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

于 2020-06-28T16:57:20.113 回答