以创建Flutter项目默认生成的代码为例来展示3D透视效果。先通过Transform来实现3D效果。代码如下:
为了出于演示的目的,将默认的布局代码通过_defaultApp方法进行了封装,然后仅仅是通过Transfrom来实现3D效果。
上面代码中,通过Transfrom来实现透视效果,而Transfrom是通过Matrix4进行矩阵变换来实现的这个效果。
由于现在的智能手机都有用于图形计算的GPU单元,对于图形的计算与渲染进行了优化,因此即使是渲染3D图形也是非常快的。因此,基本上你看到的手机上的所有图形,都是通过3D的渲染方式来呈现的,即使是2D的图形素材。
通过设置变换矩阵,可以改变我们看到的视觉效果(甚至是3D效果)。通常来讲,矩阵变换包括:平移、旋转、缩放、透视。上面代码中,我们通过identity_matrix创建了一个矩阵,然后应用给Transform。需要注意的是,矩阵变换不满足交换律,因此参数的位置要弄对,当传入矩阵之后,最后的矩阵运算结果会传递给GPU,然后对图像进行渲染。
上面代码实现了透视的效果,也就是,更远的部分,应该看起来更小一些。因此上面的参数里面,会根据距离进行0.001的缩放。
那么0.001这个参数是怎么来的?其实这个数据很随意,可以把这个数据增大或者减小看一下效果,这个数据越大,展现的效果就好像是我们越来越靠近观察对象。
Flutter也提供了一个makePerspectiveMatrix方法进行透视矩阵变换,但是这个方法需要设置一下额外的参数,这些参数我们远远用不到,因此直接使用matrix来完成矩阵变换即可。
同时,上面的代码通过_offset来指定了x轴和轴的旋转。
直接通过GestureDetector来实现手势交互。
接下来实现的效果相对复杂一点,类似翻页效果动画。
第一眼看到这个效果,可能想到的就是,通过Stack来实现,并且每一页都分成上下两部分,每一部分可以绕X轴旋转,旋转之后就会看到下一个页面。
那么该如何用代码来实现呢?可以分成两部分来进行。
那么,在Flutter中,什么样的Widget适合我们来实现这个效果呢?ClipRect和Transform。
接下来定义一个Widget来实现这个功能。
classFlipWidgetextendsStatelessWidget{Widgetchild;FlipWidget({Keykey,this.child}):super(key:key);@overrideWidgetbuild(BuildContextcontext){returnColumn(mainAxisSize:MainAxisSize.min,children:[ClipRect(child:Align(alignment:Alignment.topCenter,heightFactor:0.5,child:child,)),Padding(padding:EdgeInsets.only(top:2.0),),ClipRect(child:Align(alignment:Alignment.bottomCenter,heightFactor:0.5,child:child,)),],);}}这里面的child参数,可以传递任意类型的Widget(text,image等)。运行上面的代码,可以看到如下的效果。
Transform这个Widget组件有一个Matrix4类型的参数transform,这个参数决定了我们将应用何种类型的矩阵变换。同时,Matrix4提供了一个名字为rotationX()的构造方法,这个似乎正是我们需要的,我们把这个应用给页面的上半部分试一下。
@overrideWidgetbuild(BuildContextcontext){returnColumn(mainAxisSize:MainAxisSize.min,children:[Transform(transform:Matrix4.rotationX(pi/4),alignment:Alignment.bottomCenter,child:ClipRect(child:Align(alignment:Alignment.topCenter,heightFactor:0.5,child:child,)),),...],);}运行上面的代码。
显然,这个效果仅仅是把上半部分缩小了,不是我们想要的效果。但是如果额外再指定Matrix4的参数,让row为3,column为2,试一下效果。
Transform(transform:Matrix4.identity()..setEntry(3,2,0.006)..rotateX(pi/4),alignment:Alignment.bottomCenter,child:ClipRect(child:Align(alignment:Alignment.topCenter,heightFactor:0.5,child:child,)),),...
看起来这个是我们需要的效果,上面还一个参数,0.006,这个是怎么来的?其实是试出来的,选一个自己感觉不错的数值就行了。
接下来就是给翻转加上动画了。但是这块可能相对复杂一点。首先,每一页都要理解为有两面(正反面),但是要实现这个效果用代码可能不是很容易,因为我们在手机上看到的图像在任何时刻都只有一面。
我们假设,我们是向上翻转的,那么我们的动画可以分成两部分,第一部分是我们将下半部分向上翻转一半时,这个过程的效果是,当前翻转的页面逐渐消失,而这个页面的下一个页面会逐渐显示。第二部分是,将当前页面继续向上翻转,这个过程的效果是,当前页面会逐渐显示,上半部分的当前页面就是逐渐消失。