在Flutter中,用一组FPS绘制大量简单、充满颜色的矩形的最具性能的方法是什么



我想用大量随机的、颜色填充的矩形填充一个方形的CustomPaint Widget。动画需要以设定的FPS速率进行更新。我有一个工作演示,但即使只有100x100的随机矩阵,我手机上的最大FPS似乎也在30左右。代码需要是跨平台的(android和iOS(。

我是Flutter的新手,我想知道是否有一种更具表演性的方式来制作这部动画。逻辑一点也不复杂,但我想大量的随机值生成和数千个微小的矩形是一个及时生成的挑战。在原生android上,我使用了openGL和GLSurfaceView,我希望Flutter有同样的性能。。。

这是我的代码链接:https://gist.github.com/ize8/f734b9d62d78c74667a845f211e06fb7

import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'CustomPaint Animation'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {

bool _isRunning = true; //is the animation running?
var _fps = new List.filled(20, 0.0); //list for the last 20 FPS
int _pos = 0; //position for logging FPS
final _rnd = Random();
final _rowSize = 100;//how many rectangles in a row
final _freq = -1; //the maximum FPS, -1 if no limit
List _matrix;
Ticker _ticker;
Duration lastTick = Duration(milliseconds: 0);
@override
void initState() {
super.initState();
_matrix = _generateMatrix();//generate random set
_ticker = createTicker((Duration elapsed) {
var diff = elapsed.inMicroseconds - lastTick.inMicroseconds;//time elapsed since last tick
var currentFps = 1000000 / diff;//calculate current FPS
if (currentFps <= _freq || _freq == -1) {
setState(() {
_fps[_pos] = currentFps;//save FPS into list
lastTick = elapsed;
_matrix = _generateMatrix(); //generate new random set
});
if (_pos < _fps.length - 1)
_pos += 1;
else
_pos = 0;
}
});
_ticker.start();//start ticker
}
@override
void dispose() {
_ticker.dispose();
super.dispose();
}
//generate new random set of color codes 0-3
List _generateMatrix() {
return new List.generate(_rowSize * _rowSize, (int i) => _rnd.nextInt(4),
growable: false);
}
void _toggleDynamic(bool isRunning) {
setState(() {
_isRunning = isRunning;
});
if (isRunning)
_ticker.start();
else
_ticker.stop();
}
//calculate average FPS from list
double _getAvgFps() {
return _fps.reduce((value, element) => value + element) / _fps.length;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: OrientationBuilder(builder: (context, orientation) {
return Center(
child: Container(
color: Colors.grey[850],
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Run!",
style: TextStyle(
color: Colors.blue[300],
fontWeight: FontWeight.bold,
fontSize: 20.0),
),
Switch(
value: _isRunning,
onChanged: (bool val) => _toggleDynamic(val)
),
],
),
Divider(),
Container(
width: 400,
height: 400,
decoration: BoxDecoration(color: Colors.white, boxShadow: [
BoxShadow(
color: Colors.black,
blurRadius: 4.0,
spreadRadius: 2.0,
)
]),
child: CustomPaint(
painter: MyPainter(_matrix, 400/_rowSize, _rowSize),//fill out the 400x400 container
isComplex: true,
willChange: true,
),
),
Divider(),
Text(
"${_getAvgFps().toStringAsFixed(2)} FPS",
style: TextStyle(color: Colors.blue[300]),
),
],
),
),
);
}),
);
}
}
class MyPainter extends CustomPainter {
List _matrix;
double _pixSize;
int _rowSize;
final redPaint = Paint()..color = Colors.red;
final greenPaint = Paint()..color = Colors.green[900];
final bluePaint = Paint()..color = Colors.blueGrey;
final purplePaint = Paint()..color = Colors.purple;
Paint getPaint(int code) {
switch (code) {
case 0:
return bluePaint;
break;
case 1:
return purplePaint;
break;
case 2:
return greenPaint;
break;
case 3:
return redPaint;
break;
default:
return bluePaint;
}
}
MyPainter(List matrix, double pixSize, int rowSize) {
this._matrix = matrix;
this._pixSize = pixSize;
this._rowSize = rowSize;
}
@override
void paint(Canvas canvas, Size size) {
for (var i = 0; i < _matrix.length; i++) {
var col = (i / _rowSize).floor();
var row = i % _rowSize;
canvas.drawRect(
Rect.fromLTWH(col * _pixSize, row * _pixSize, _pixSize, _pixSize),
getPaint(_matrix[i]));
}
}
@override
bool shouldRepaint(MyPainter oldDelegate) {
return false;
}
}

也许生成位图作为原始字节码并将其显示在画布上而不是绘制所有矩形会更快?

p.S.[1]:我发现了decodeImageFromPixels(),它看起来很有前景,尽管同时也是一个PITA。。。

decodeImageFromPixels(),然后将回调中的图像绘制到CustomPaint上,提供了令人满意的性能提升。虽然仍然没有达到Android+GLSurfaceView的水平,但它仍然可以接受:(

最新更新