使用Flutter中实现画中画效果,这操作66的

640?wx_fmt=jpeg


/   今日科技快讯   /


近日,北京移动方面表示,到今年年底5G基站将超过8000个,实现对东、西、北五环和南四环内,以及郊区城区的覆盖。北京电信也披露年底前也将实现五环内5G信号在室外的连续覆盖,明年起加快五环外重点区域的信号覆盖。


/   作者简介   /


大家周一好,新的一周继续努力吧!

本篇文章来自北斗星_And的投稿,分享了如何用Flutter实现Android中的画中画效果,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。


北斗星_And的博客地址:

https://juejin.im/user/5d2ef4f7f265da1bb47d9a07


/   前言   /


今天讲Flutter的实现篇,画中画效果的实现。先看一下PIP的实现效果。


使用Flutter中实现画中画效果,这操作66的_第1张图片


使用Flutter中实现画中画效果,这操作66的_第2张图片


更多效果请查看demo,代码地址为:

https://github.com/DingProg/FlutterPIP


/   为什么会有此文?   /


一天在浏览朋友圈时,发现了一个朋友发了一张图(当然不是女朋友,但是个女的),类似上面效果部分。一看效果挺牛啊,这是怎么实现的呢?心想要不自己实现一下吧?于是开始准备用Android实现一下。


但最近正好学了一下Flutter,并在学习Flutter 自定义View CustomPainter时,发现了和Android上有相同的API,Canvas,Paint,Path等. 查看Canvas的绘图部分drawImage代码如下:




可以看出drawImage 调用了内部的_drawImage,而内部的_drawImage使用的是native Flutter Engine的代码 'Canvas_drawImage',交给了Flutter Native去绘制。那Canvas的绘图就可以和移动端的Native一样高效 (Flutter的绘制原理,决定了Flutter的高效性)。


/   实现步骤   /


看效果从底层往上层,图片被分为3个部分,第一部分是底层的高斯模糊效果,第二层是原图被裁剪的部分,第三层是一个效果遮罩。


Flutter 高斯模糊效果的实现


Flutter提供了BackdropFilter,关于BackdropFilter的官方文档是这么说的:

A widget that applies a filter to the existing painted content and then paints child.


The filter will be applied to all the area within its parent or ancestor widget's clip. If there's no clip, the filter will be applied to the full screen.


简单来说,他就是一个筛选器,筛选所有绘制到子内容的小控件,官方demo例子如下:




效果就是对中间200*200大小的地方实现了模糊效果。本文对底部图片高斯模糊效果的实现如下:




其中Container的大小和图片大小一致,并且Container需要有子控件,或者背景色。其中子控件和背景色可以任意。实现效果如图


使用Flutter中实现画中画效果,这操作66的_第3张图片


Flutter 图片裁剪


图片裁剪原理


在用Android中的Canvas进行绘图时,可以通过使用PorterDuffXfermode将所绘制的图形的像素与Canvas中对应位置的像素按照一定规则进行混合,形成新的像素值,从而更新Canvas中最终的像素颜色值,这样会创建很多有趣的效果。


Flutter 中也有相同的API,通过设置画笔Paint的blendMode属性,可以达到相同的效果.混合模式具体可以Flutter查看官方文档,有示例。


此处用到的混合模式是BlendMode.dstIn,文档注释如下:

/// Show the destination image, but only where the two images overlap. The /// source image is not rendered, it is treated merely as a mask. The color /// channels of the source are ignored, only the opacity has an effect. /// To show the source image instead, consider [srcIn]. // To reverse the semantic of the mask (only showing the source where the /// destination is present, rather than where it is absent), consider [dstOut]. /// This corresponds to the "Destination in Source" Porter-Duff operator.


使用Flutter中实现画中画效果,这操作66的_第4张图片


大概说的意思就是,只在源图像和目标图像相交的地方绘制【目标图像】,绘制效果受到源图像对应地方透明度影响。


用Android里面的一个公式表示为:




实际裁剪


我们要用到一个Frame图片(frame.png),用来和原图进行混合,Frame图片如下:


使用Flutter中实现画中画效果,这操作66的_第5张图片


实现代码如下所示:




分为三个主要步骤:


  • 第一个步骤,加载原图和Frame图片,使用Future.wait 等待两张图片都加载完成

  • 原图进行缩放,平移处理,缩放至frame合适大小,在将图片平移至图片中央

  • 设置paint的混合模式,绘制Frame图片,完成裁剪


裁剪后的效果图如下:


使用Flutter中实现画中画效果,这操作66的_第6张图片


Flutter 图片合成及保存


裁剪完的图片和效果图片(mask.png)的合成。先看一下mask图片长啥样:


使用Flutter中实现画中画效果,这操作66的_第7张图片


裁剪完的图片和mask图片的合成,不需要设置混合模式,裁剪图片在底层,合成完的图片在上层。既可实现,但需要注意的是,裁剪的图片需要画到效果区域,所以x,y需要有偏移量,实现代码如下:




效果实现


本文开始介绍了,图片分为三层,所以此处使用了Stack组件来包装PIP图片:






图片保存


Flutter 是一个跨平台的高性能UI框架,使用到Native Service的部分,需要各自实现,此处需要把图片保存到本地,使用了一个库,用于获取各自平台的可以保存文件的文件路径。




实现步骤,先将上面的PIP用一个RepaintBoundary 组件包裹,然后通过给RepaintBoundary设置key,再去截图保存,实现代码如下:




截屏保存代码如下所示:




显示图片的保存路径的代码如下所示:



/   手势交互实现思路   /


目前的实现方式是:把原图移动到中央进行裁剪,默认认为图片的重要显示区域在中央,这样就会存在一个问题,如果图片的重要显示区域没有在中央,或者画中画效果的显示区域不在中央,会存在一定的偏差。


所以需要添加手势交互,当图片重要区域不在中央,或者画中画效果不在中央,可以手动调整显示区域。


实现思路:添加手势操作,获取当前手势的offset,重新拿原图和frame区域进行裁剪,就可以正常显示。(目前暂未去实现)


项目地址为:

https://github.com/DingProg/FlutterPIP


推荐阅读:

7张图带你轻松理解Java 线程安全

好奇一键登录是怎么实现的吗?进来了解一下?

由Android官方团队带你学习布局编辑器


欢迎关注我的公众号

学习技术或投稿


640.png?


640?wx_fmt=jpeg

长按上图,识别图中二维码即可关注


你可能感兴趣的:(使用Flutter中实现画中画效果,这操作66的)