在Flutter中将图像转换为灰度:ColorFiltered组件

背景

在最近的项目中,我遇到了一个需求:当满足特定条件时,需要将图像转换为灰度。例如,用户个人资料页面有一组用来显示个人社交信息,并展示相关社交平台图标,如果用设置了社交信息,则该社交平台的图标应处理点亮状态,如果没有设置,则应处于禁用状态,也就是是说,我需要把对应图标的颜色改为灰度模式。这看起来很简单,特别是对于那些单一颜色的图标,对吧?但实际上,有一些图标是彩色的而且色彩丰富,比如说instagram。

BlendMode

我首先研究了Flutter中的混合模式(BlendMode),结果发现它们确实效果很好 ------ 但这仅适用于没有透明度的JPEGPNG 图像。以下是我用于将彩色透明PNG图像显示为灰度图像的代码。

dart 复制代码
ColorFiltered(
        colorFilter: enabled ? ColorFilter.mode(
          Colors.transparent,
          BlendMode.multiply,
        ) : ColorFilter.mode(
          Colors.grey,
          BlendMode.saturation,
        ),
        child: Image.asset("assets/roundabout.png",),
      )

上面用的图片是一个带有透明的PNG图片,让我们看看效果:

再放一个没有经过处理的demo用的原图:

很显然,这样的效果很糟糕。混合模式(BlendMode)也会影响图像中的透明部分,并将它们也转换为灰色,但我们需要让这些透明区域也不受影响。

颜色矩阵

经过一番研究,我在Flutter文档的深处发现了这个内容。文档中提到了另一个构造函数,它可以用来定义一个矩阵,从而更精细地控制混合模式的应用方式。

于是,我用ColorFilter.matrix替换了ColorFilter.mode构造函数,并传入了这些神奇的数字。

dart 复制代码
ColorFilter.matrix(<double>[
 0.2126,0.7152,0.0722,0,0,
 0.2126,0.7152,0.0722,0,0,
 0.2126,0.7152,0.0722,0,0,
 0,0,0,1,0,
])

没错,这正是我一直在寻找的解决方案。文档中还提到了更多示例。大家可以随意尝试这些示例,并对这些数值进行调整。

** 反色(Inversion)** 效果的颜色矩阵:

dart 复制代码
const ColorFilter invert = ColorFilter.matrix(<double>[
   -1, 0, 0, 0, 255,
   0, -1, 0, 0, 255,
   0, 0, -1, 0, 255,
   0, 0, 0, 1, 0, ]);

** 复古棕褐色(Sepia Tone)** 效果的颜色矩阵

dart 复制代码
const ColorFilter sepia = ColorFilter.matrix(<double>[ 
   0.393, 0.769, 0.189, 0, 0, 
   0.349, 0.686, 0.168, 0, 0,
   0.272, 0.534, 0.131, 0, 0, 
   0, 0, 0, 1, 0, ]);

个人感觉这个效果适用于复古风格 UI、老照片效果、特定主题界面

灰度滤镜(Grayscale Color Filter) 效果矩阵

标准灰度滤镜矩阵(最常用)

dart 复制代码
const ColorFilter greyscale = ColorFilter.matrix(<double>[ 
  0.2126, 0.7152, 0.0722, 0, 0, // 红色通道转换
  0.2126, 0.7152, 0.0722, 0, 0, // 绿色通道转换
  0.2126, 0.7152, 0.0722, 0, 0, // 蓝色通道转换
  0, 0, 0, 1, 0, //透明度保持不变
  ]);

上面的的灰度算法是一个标准的灰度算法,还两种算法可供参考:

简化版灰度矩阵(平均法)

dart 复制代码
ColorFilter.matrix([ 
0.333, 0.333, 0.333, 0, 0, // 红色通道取平均值 
0.333, 0.333, 0.333, 0, 0, // 绿色通道取平均值 
0.333, 0.333, 0.333, 0, 0, // 蓝色通道取平均值 
0, 0, 0, 1, 0, // 透明度保持不变 
])

高对比度灰度矩阵

dart 复制代码
ColorFilter.matrix([ 
0.299, 0.587, 0.114, 0, 0, // 红色通道(电视标准算法) 
0.299, 0.587, 0.114, 0, 0, // 绿色通道 
0.299, 0.587, 0.114, 0, 0, // 蓝色通道 
0, 0, 0, 1, 0, // 透明度保持不变
])

矩阵原理

  • 人眼对红、绿、蓝三色的敏感度不同,公式 亮度 = 0.2126*R + 0.7152*G + 0.0722*B 可模拟真实灰度效果。
  • 矩阵中每行对应一个颜色通道(红、绿、蓝、透明度),每列对应输入颜色的分量(R、G、B、A、偏移量)。

让我们再回头看看BlendMode相关属性:

关键属性解析

属性 说明
colorFilter 必需参数,指定颜色滤镜。可通过 ColorFilter.modeColorFilter.matrix 创建。
BlendMode 混合模式(如 colorBurnsrcIn 等),影响颜色过滤的最终效果。
child 待处理的子组件(通常为 ImageIcon 等可视化组件)。

注意事项

  1. 性能影响

    • 复杂的 ColorFilter.matrix 可能对性能产生轻微影响,建议对静态图像提前处理为灰度图。
    • 动态切换时,避免在列表或高频率刷新场景中大量使用。
  2. 混合模式选择

    • BlendMode.color 可保留亮度,仅修改色相和饱和度,适合部分灰度效果。
    • BlendMode.luminosity 直接提取亮度,是最纯净的灰度转换模式。

最后

通过 ColorFiltered 组件,Flutter 提供了简洁高效的图像灰度处理方案。无论是静态效果还是动态交互,合理选择混合模式或矩阵运算,均可灵活实现所需视觉效果。

也希望大家关注我的微信公众号OpenFlutter

相关推荐
不想上班只想要钱17 分钟前
vue3+vite创建的项目,运行后没有 Network地址
前端·javascript·vue.js
流***陌35 分钟前
手办盲盒抽赏小程序前端功能设计:兼顾收藏需求与抽赏乐趣
前端·小程序
岁月宁静1 小时前
在富文本编辑器中封装实用的 AI 写作助手功能
前端·vue.js·人工智能
金士顿1 小时前
为什么MainWindow.xaml绑定的datacontext,EtherCATSuiteCtrl.xaml直接用了?
前端
533_1 小时前
[css] flex布局中的英文字母不换行问题
前端·css
浮游本尊1 小时前
React 18.x 学习计划 - 第四天:React Hooks深入
前端·学习·react.js
future_studio2 小时前
聊聊 Unity(小白专享、C# 小程序 之 日历、小闹钟)
前端·html
Yeats_Liao2 小时前
Go Web 编程快速入门 · 04 - 请求对象 Request:头、体与查询参数
前端·golang·iphone
祈祷苍天赐我java之术2 小时前
Redis 数据类型与使用场景
java·开发语言·前端·redis·分布式·spring·bootstrap
草莓熊Lotso3 小时前
C++ 方向 Web 自动化测试入门指南:从概念到 Selenium 实战
前端·c++·python·selenium