1. 背景
最近一个需求改版UI视觉觉得微信朋友圈的边际高斯含糊挺好看,然后就苦逼吭哧的测验在Flutter完成了,来看微信朋友圈点击展开的大图作用图:
微信朋友圈高斯含糊作用大约分4部分区域完成,如下图:
居中图片为原始图,然后背景含糊全图是原始图扩大cover形式的高斯含糊,在上下两个区域分别是两层单独处理鸿沟的高斯含糊作用特别处理,因而有时分能够看到微信朋友圈在上下两边有明显分界线;
2. 实践
在Flutter侧完成高斯含糊比较简略,能够直接运用体系的BackdropFilter函数完成,需求传入一个filter方法,然后对child区域进行含糊过滤;
const BackdropFilter({
Key? key,
required this.filter,
Widget? child,
this.blendMode = BlendMode.srcOver,
}) : assert(filter != null),
super(key: key, child: child);
Flutter提供了简化ImageFiltered完成高斯含糊,代码如下:
ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Image.network(url,fit: BoxFit.cover, height: expandedHeight, width: width),
),
通过此方法,能够十分简约完成全屏高斯含糊~,现在难点是上下鸿沟区域的鸿沟含糊处理,这儿需求运用一个ShaderMask组件,在Flutter侧ShaderMask主要是完成突变过渡才能的;
const ShaderMask({
Key? key,
required this.shaderCallback,
this.blendMode = BlendMode.modulate,
Widget? child,
}) : assert(shaderCallback != null),
assert(blendMode != null),
super(key: key, child: child);
其需求shaderCallback回调突变Shader,共提供3种突变形式:
- RadialGradient:放射状突变
- LinearGradient:线性突变
- SweepGradient:扇形突变
这儿咱们需求运用线性突变LinearGradient从上到下的突变过渡,代码如下:
ShaderMask(
shaderCallback: (Rect bounds) {
return const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.white,
Colors.transparent
],
).createShader(bounds);
},
child: Image.network(url,
fit: BoxFit.cover, height: closeHeight, width: width),
)
就这样完成了?当我运行时分呈现如下作用,作用还挺好的:
可是当我把封面图url替换了一个淡色图片,却呈现如下作用,中间区域变成了黑色的,看来是我想的简略了:
剖析了下Flutter线性过度源码,其将色彩进行过渡, Color transparent = Color(0x00000000) , 而 Color white = Color(0xFFFFFFFF),能够看到除了透明度之外,需求确保色彩不要产生大改变,其实咱们诉求仅仅需求将透明度产生突变即可,因而将Colors.white改为Colors.black,
ShaderMask(
shaderCallback: (Rect bounds) {
return const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black,
Colors.transparent
],
).createShader(bounds);
},
child: Image.network(url,
fit: BoxFit.cover, height: closeHeight, width: width),
)
呈现如下作用:
这儿色彩形似契合预期,可是混合形式呈现了问题,学过Android开发的一定特点如下这张BlendMode混合形式图片:
ShaderMaster默认的混合形式是BlendMode.modulate,这个我也解说不清楚:这儿有一篇相关文章/post/684490…
这儿咱们将混合形式替换为BlendMode.dstIn:只显示src和dst重合部分,且src的重合部分只要不透明度有用,通过这些操作后,整体作用最终如下所示:
最终奉上完好demo的相关代码:
Widget buildCover(BuildContext context) {
double width = MediaQuery.of(context).size.width;
double expandedHeight = 600;
double closeHeight = 300;
const String url =
'https://www.6hu.cc/wp-content/uploads/2022/12/1670961338-0421f1cf6f58e9e.png';
return Container(
height: expandedHeight,
alignment: Alignment.center,
child: Stack(
children: [
ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Image.network(url,
fit: BoxFit.cover, height: expandedHeight, width: width),
),
Container(
height: expandedHeight,
alignment: Alignment.center,
child: ShaderMask(
shaderCallback: (Rect bounds) {
return const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black,
Colors.transparent
],
).createShader(bounds);
},
blendMode: BlendMode.dstIn,
child: Image.network(url,
fit: BoxFit.cover, height: closeHeight, width: width),
),
)
],
),
);
}
3. 总结
通过实践,发现Flutter完成高斯含糊BackdropFilter/ImageFiltered组件,突变完成方法ShaderMask,此外还需求把握图形学的BlendMode混合形式,以后在碰到相似需求时分主张直接砍了UI视觉吧~~费力~~~~