Flutter 颤振:如何使用CustomPainter创建生成动画

Flutter 颤振:如何使用CustomPainter创建生成动画,flutter,dart,generative-art,Flutter,Dart,Generative Art,我使用了Flatter的CustomPainter类来创建一个使用路径生成的静态图像(见下面的代码)。我希望能够无限期地为这些图像制作动画。最直接的方法是什么 import 'package:flutter/material.dart'; void main() => runApp( MaterialApp( home: PathExample(), ), ); class PathExample extends StatelessWid

我使用了Flatter的CustomPainter类来创建一个使用路径生成的静态图像(见下面的代码)。我希望能够无限期地为这些图像制作动画。最直接的方法是什么

import 'package:flutter/material.dart';

void main() => runApp(
      MaterialApp(
        home: PathExample(),
      ),
    );

class PathExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: PathPainter(),
    );
  }
}

class PathPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.grey[200]
      ..style = PaintingStyle.fill
      ..strokeWidth = 0.0;

    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint);

    Path path2 = Path();
    for (double i = 0; i < 200; i++) {
      Random r = new Random();
      path2.moveTo(sin(i / 2.14) * 45 + 200, i * 12);
      path2.lineTo(sin(i / 2.14) * 50 + 100, i * 10);
      paint.style = PaintingStyle.stroke;
      paint.color = Colors.red;
      canvas.drawPath(path2, paint);
    }

    Path path = Path();
    paint.color = Colors.blue;
    paint.style = PaintingStyle.stroke;
    for (double i = 0; i < 30; i++) {
      path.moveTo(100, 50);
      // xC, yC, xC, yC, xEnd, yEnd
      path.cubicTo(
          -220, 300, 500, 600 - i * 20, size.width / 2 + 50, size.height - 50);
      canvas.drawPath(path, paint);
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}
导入“包装:颤振/材料.省道”;
void main()=>runApp(
材料聚丙烯(
home:PathExample(),
),
);
类PathExample扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回自定义油漆(
painter:pathpainer(),
);
}
}
类PathPainter扩展了CustomPainter{
@凌驾
空心油漆(帆布,尺寸){
油漆油漆=油漆()
…颜色=颜色。灰色[200]
…style=PaintingStyle.fill
..冲程宽度=0.0;
canvas.drawRect(Rect.fromLTWH(0,0,size.width,size.height),paint);
路径路径2=路径();
对于(双i=0;i<200;i++){
随机r=新随机();
路径2.移动到(sin(i/2.14)*45+200,i*12);
路径2.lineTo(sin(i/2.14)*50+100,i*10);
paint.style=PaintingStyle.stroke;
paint.color=Colors.red;
画布绘制路径(路径2,绘制);
}
路径=路径();
paint.color=Colors.blue;
paint.style=PaintingStyle.stroke;
对于(双i=0;i<30;i++){
路径移动到(100,50);
//xC,yC,xC,yC,xEnd,yEnd
path.cubicTo(
-220、300、500、600-i*20,尺寸.宽度/2+50,尺寸.高度-50);
画布.绘制路径(路径,绘制);
}
}
@凌驾
bool shouldRepaint(CustomPainter oldDelegate)=>true;
}

要做到这一点,您需要做大量的
TickerProviderStateMixin
所做的工作-本质上,您需要创建和管理自己的
Ticker

我已经在下面一个简单的构建器小部件中完成了这项工作。它只是在每次出现滴答声时安排一个构建,然后在该记录单期间使用给定的值进行构建。为了方便起见,我添加了一个
totalappeased
参数以及一个
sincellastdraw
参数,但您可以根据您所做的事情最方便的方式轻松地选择其中一个

import'dart:math';
进口“包装:颤振/材料.省道”;
导入“package:flatter/scheduler.dart”;
void main()=>runApp(
材料聚丙烯(
home:PathExample(),
),
);
类PathExample扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回TickerBuilder(builder:(上下文、sinceLast、总计){
返回自定义油漆(
painter:PathPainter(总毫秒数/1000.0),
);
});
}
}
类TickerBuilder扩展StatefulWidget{
//此生成器函数用于创建执行以下操作的小部件
//根据经过的时间或
//自上次构建以来的时间。前者对于基于位置的
//动画,而后者可用于基于速度的
//动画(即旧位置+(时间*速度)=新位置)。
最终小部件功能(BuildContext上下文、持续时间sincellastdraw、持续时间totalappeased)生成器;
const TickerBuilder({Key?Key,需要this.builder}):super(Key:Key);
@凌驾
_TickerBuilderState createState()=>\u TickerBuilderState();
}  
类_TickerBuilderState扩展状态{
//创建一个ticker,确保每帧调用onTick函数
迟交的最终报价单_Ticker=报价单(onTick);
//总时间是小部件创建后经过的时间。
//它最初设置为零,因为首次创建时没有时间弹性。
持续时间总计=持续时间0;
//最后一次绘制时间在每个绘制周期中保存;这是为了
//可以计算绘制之间的时间间隔
持续时间lastDraw=Duration.zero;
void onTick(持续时间){
//通过每次调用此函数时调用setState,我们
//触发此小部件在每个帧上重建。
//这就是无限动画部分的用武之地!
设置状态(){
总计=经过的时间;
});
}
@凌驾
void initState(){
super.initState();
_ticker.start();
}
@凌驾
void didChangeDependencies(){
_ticker.muted=!TickerMode.of(上下文);
super.didChangeDependencies();
}
@凌驾
小部件构建(构建上下文){
最终结果=widget.builder(context,total-lastDraw,total);
lastDraw=总计;
返回结果;
}
@凌驾
无效处置(){
_停止();
super.dispose();
}
}
类PathPainter扩展了CustomPainter{
最终双pos;
PathPainter(this.pos);
@凌驾
空心油漆(帆布,尺寸){
油漆油漆=油漆()
…颜色=颜色。灰色
…style=PaintingStyle.fill
..冲程宽度=0.0;
canvas.drawRect(Rect.fromLTWH(0,0,size.width,size.height),paint);
路径路径2=路径();
对于(双i=0;i<200;i++){
随机r=新随机();
路径2.移动到(sin(i/2.14+pos)*45+200(i*12));
路径2.lineTo(sin(i/2.14+pos)*50+100(i*10));
paint.style=PaintingStyle.stroke;
paint.color=Colors.red;
画布绘制路径(路径2,绘制);
}
路径=路径();
paint.color=Colors.blue;
paint.style=PaintingStyle.stroke;
对于(双i=0;i<30;i++){
路径移动到(100,50);
//xC,yC,xC,yC,xEnd,yEnd
path.cubicTo(
-220,
300,
500,
600-i*20,
尺寸/宽度/2+50,
尺寸。高度-50,
);
画布.绘制路径(路径,绘制);
}
}
//在这种特殊情况下,这是相当多余的
//动画每一帧都在发生。但是,
//在不需要为每一帧设置动画的情况下,您可以
//应该实现它,使其仅在实际
//需要重新绘制,因为这样可以优化颤振引擎
//它的绘图和使用较少