Flutter渐变粒子颜色插值:避免频繁赋值ColorFilter的优化方案
如何避免频繁创建ColorFilter导致Web端GC峰值?
我实现了一个继承自ImageParticle的粒子类,通过在update方法中给Paint对象重新赋值ColorFilter,实现从_start到_end的颜色渐变效果。但Web端频繁创建ColorFilter会引发GC峰值,影响性能,官方的ComputedParticle方案也存在同样问题,想知道有没有无需每次更新都重新赋值ColorFilter的颜色插值实现方式。
现有代码如下:
class Particle extends ImageParticle { final Color _start; final Color _end; final Paint _paint; Particle({ required super.image, required Color start, required Color end, super.lifespan, }) : _start = start, _end = end, _paint = Paint(); @override void render(Canvas canvas) { canvas.scale(2 * Curves.decelerate.transform(1 - progress)); canvas.drawImageRect( image, src, dest, _paint, ); } @override void update(double dt) { _paint.colorFilter = ColorFilter.mode( Color.lerp( _start, _end, Curves.decelerate.transform(progress), )!, BlendMode.modulate, ); super.update(dt); } }
优化方案:用Paint的color + blendMode替代ColorFilter
原实现通过ColorFilter.mode实现颜色叠加,等价于直接设置Paint的blendMode为modulate,并动态修改color属性。这种方式避免了频繁创建ColorFilter对象(带有Native层资源的重型对象),转而操作轻量的Color值类型,能大幅降低Web端GC压力。
修改后的代码:
class Particle extends ImageParticle { final Color _start; final Color _end; final Paint _paint; Particle({ required super.image, required Color start, required Color end, super.lifespan, }) : _start = start, _end = end, // 提前设置混合模式,无需重复创建 _paint = Paint()..blendMode = BlendMode.modulate; @override void render(Canvas canvas) { canvas.scale(2 * Curves.decelerate.transform(1 - progress)); canvas.drawImageRect( image, src, dest, _paint, ); } @override void update(double dt) { // 直接修改color,避免创建ColorFilter _paint.color = Color.lerp( _start, _end, Curves.decelerate.transform(progress), )!; super.update(dt); } }
进一步优化:手动计算颜色插值(可选)
如果想进一步减少Color对象的创建频率,可以手动拆分颜色分量进行插值,直接生成最终颜色值:
class Particle extends ImageParticle { final int _startAlpha; final int _startRed; final int _startGreen; final int _startBlue; final int _endAlpha; final int _endRed; final int _endGreen; final int _endBlue; final Paint _paint; Particle({ required super.image, required Color start, required Color end, super.lifespan, }) : _startAlpha = start.alpha, _startRed = start.red, _startGreen = start.green, _startBlue = start.blue, _endAlpha = end.alpha, _endRed = end.red, _endGreen = end.green, _endBlue = end.blue, _paint = Paint()..blendMode = BlendMode.modulate; @override void render(Canvas canvas) { canvas.scale(2 * Curves.decelerate.transform(1 - progress)); canvas.drawImageRect( image, src, dest, _paint, ); } @override void update(double dt) { final t = Curves.decelerate.transform(progress); // 手动计算ARGB插值,减少Color对象创建 final alpha = (_startAlpha + (_endAlpha - _startAlpha) * t).round(); final red = (_startRed + (_endRed - _startRed) * t).round(); final green = (_startGreen + (_endGreen - _startGreen) * t).round(); final blue = (_startBlue + (_endBlue - _startBlue) * t).round(); _paint.color = Color.fromARGB(alpha, red, green, blue); super.update(dt); } }
原理说明
ColorFilter是包含Native层资源的重型对象,每次创建都会在Web端产生额外的GC负担;而Color是基于整数的轻量值类型,即使每次更新创建新实例,GC压力也远低于ColorFilter。提前设置blendMode为modulate,视觉效果与原实现完全一致,同时消除了频繁创建ColorFilter的性能问题。
内容的提问来源于stack exchange,提问作者anonymous-dev
相关产品推荐
相关产品推荐

