为什么用strokeWidth绘制Flutter Path太昂贵了?

时间:2019-09-21 13:20:17

标签: flutter flutter-animation

我有一个CustomPainter可以在屏幕截图中画出这种心形,但是当我做一个简单的动画(例如缩放)时,我发现动画滞后了(它掉了很多帧),但是令人惊讶的是当我不要将笔划宽度设置为绘制对象,动画会顺利进行。

我的问题是: 如何设置笔触宽度并同时获得平滑的动画?

secreenshot

这是完整的应用程序:

import 'package:flutter/material.dart';

void main() {
  runApp(Page());
}

class Page extends StatefulWidget {
  @override
  _PageState createState() => _PageState();
}

class _PageState extends State<Page> with TickerProviderStateMixin {
  AnimationController _controller;

  var scale = 1.0;

  @override
  void initState() {
    _controller = AnimationController(
        vsync: this,
        lowerBound: 1.0,
        upperBound: 2.0,
        duration: Duration(seconds: 3));
    _controller.addListener(() {
      setState(() {
        scale = _controller.value;
      });
    });

    super.initState();
  }

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

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Expanded(
          child: Center(
            child: Container(
              width: 130,
              height: 200,
              child: GestureDetector(
                onTap: () => _controller.forward(from: 0.0),
                child: Transform.scale(
                  scale: scale,
                  child: CustomPaint(
                    painter: HeartsPainter(),
                  ),
                ),
              ),
            ),
          ),
        ),
        Text("Tap to animate", textDirection: TextDirection.ltr),
      ],
    );
  }
}

class HeartsPainter extends CustomPainter {
  final Paint hearPaint = Paint()
    ..strokeWidth = 1
    ..style = PaintingStyle.stroke;

  @override
  void paint(Canvas canvas, Size size) {
    print('Hearts Painting..');

    const int count = 100;
    for (int i = 0; i < count; i++) {
      _drawHeart(
          canvas,
          Size((i + 1) / count * size.width, (i + 1) / count * size.height),
          Colors.blue.withRed((i / count * 255).toInt()).withOpacity(0.4));
    }
  }

  void _drawHeart(Canvas canvas, Size size, Color color) {
    final Path path = Path();
    final double width = size.width;
    final double height = size.height;

    // Starting point
    path.moveTo(width / 2, height / 5);

    // Upper left path
    path.cubicTo(5 * width / 14, 0, 0, height / 15, width / 28, 2 * height / 5);

    // Lower left path
    path.cubicTo(width / 14, 2 * height / 3, 3 * width / 7, 5 * height / 6,
        width / 2, height);

    // Lower right path
    path.cubicTo(4 * width / 7, 5 * height / 6, 13 * width / 14, 2 * height / 3,
        27 * width / 28, 2 * height / 5);

    // Upper right path
    path.cubicTo(width, height / 15, 9 * width / 14, 0, width / 2, height / 5);

    canvas.drawPath(path, hearPaint..color = color);
  }

  @override
  bool shouldRepaint(HeartsPainter oldDelegate) => false;
}

1 个答案:

答案 0 :(得分:0)

  

我的问题是:如何设置笔触宽度并变得平滑   动画同时播放?

  1. 使用AnimatedBuilder而不是setState,可以在AnimatedBuilder中将预构建窗口小部件作为子参数传递。

      

    使用此预建子项完全是可选的,但可以改善   在某些情况下表现出色,因此是一个很好的选择   练习。

  2. 在循环之前创建心脏路径。

  3. 使用另一个画布对象来变换心脏。

  4. 别忘了产品发布会更快。

    import 'dart:ui';
    
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(Page());
    }
    
    class Page extends StatefulWidget {
      @override
      _PageState createState() => _PageState();
    }
    
    class _PageState extends State<Page> with TickerProviderStateMixin {
      AnimationController _controller;
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      void initState() {
        _controller = AnimationController(
          vsync: this,
          lowerBound: 1.0,
          upperBound: 2.0,
          duration: Duration(seconds: 3),
        );
    
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: <Widget>[
            Expanded(
              child: Center(
                child: Container(
                  width: 130,
                  height: 200,
                  child: GestureDetector(
                    onTap: () => _controller.forward(from: 0.0),
                    child: AnimatedBuilder(
                      animation: _controller,
                      child: CustomPaint(
                        painter: HeartsPainter(),
                      ),
                      builder: (context, child) {
                        return Transform.scale(
                          scale: _controller.value,
                          child: child,
                        );
                      },
                    ),
                  ),
                ),
              ),
            ),
            Text("Tap to animate", textDirection: TextDirection.ltr),
          ],
        );
      }
    }
    
    class HeartsPainter extends CustomPainter {
      final Paint heartPaint = Paint()
        ..strokeWidth = 1
        ..style = PaintingStyle.stroke;
    
      @override
      void paint(Canvas mainCanvas, Size size) {
        var heartRecordeer = PictureRecorder();
    
        var canvas = Canvas(heartRecordeer);
        var path = _drawHeart(Size(size.width, size.height));
    
        var count = 100;
        for (var i = 0; i < count; i++) {
          canvas.transform(
            (Matrix4.identity()..scale(98 / 100)).storage,
          );
          canvas.drawPath(
            path,
            heartPaint..color = Colors.blue.withRed((i / count * 255).toInt()).withOpacity(0.4),
          );
        }
    
        var picture = heartRecordeer.endRecording();
    
        mainCanvas.drawPicture(picture);
      }
    
      @override
      bool shouldRepaint(HeartsPainter oldDelegate) => false;
    
      Path _drawHeart(Size size) {
        final Path path = Path();
        final double width = size.width;
        final double height = size.height;
    
        // Starting point
        path.moveTo(width / 2, height / 5);
    
        // Upper left path
        path.cubicTo(5 * width / 14, 0, 0, height / 15, width / 28, 2 * height / 5);
    
        // Lower left path
        path.cubicTo(width / 14, 2 * height / 3, 3 * width / 7, 5 * height / 6, width / 2, height);
    
        // Lower right path
        path.cubicTo(4 * width / 7, 5 * height / 6, 13 * width / 14, 2 * height / 3, 27 * width / 28,
            2 * height / 5);
    
        // Upper right path
        path.cubicTo(width, height / 15, 9 * width / 14, 0, width / 2, height / 5);
        return path;
      }
    }