Flutter仿网易怎么实现广告卡片3D翻转效果
导读:本文共4703字符,通常情况下阅读需要16分钟。同时您也可以点击右侧朗读,来听本文内容。按键盘←(左) →(右) 方向键可以翻页。
摘要: 先看下网易新闻的效果:实现思路1、获取各种距离看图:思路: 如上图,状态栏高度和AppBar的高度我们都可以得到,屏幕的高度我们也可以得到,那么自然我们就可以计算出内容区域的高度,拿到内容区域高度我们先放到一边,接下来我们需要获取广告区域距离AppBar的距离,这是一个进行翻转核心数据,这里我们可以通过GlobalKey获取这个组件的渲染对象RenderObje... ...
目录
(为您整理了一些要点),点击可以直达。先看下网易新闻的效果:
实现思路
1、获取各种距离
看图:
思路: 如上图,状态栏高度和AppBar
的高度我们都可以得到,屏幕的高度我们也可以得到,那么自然我们就可以计算出内容区域的高度,拿到内容区域高度我们先放到一边,接下来我们需要获取广告区域距离AppBar
的距离,这是一个进行翻转核心数据,这里我们可以通过GlobalKey
获取这个组件的渲染对象RenderObject
并转化为RenderBox
,通过RenderBox
我们可以获取到这个组件在屏幕上的坐标,这样我们拿到这个坐标Y轴的值就是当前组件距离顶部的距离
核心代码:
//这里我们获取相对于屏幕左上角组件的坐标y轴GlobalKey_globalKey=GlobalKey();RenderBox?renderBox=_globalKey.currentContext?.findRenderObject()asRenderBox?;double?dy=renderBox?.localToGlobal(Offset.zero).dy;
接下来我们就可以计算出几个关键数据:
状态栏高度:stateHeight = MediaQuery.of(context).padding.top;
已知。
AppBar高度:appBarHeight = 56; 默认高度 已知。
内容区域高度:contentHeight = MediaQuery.of(context).size.height - stateHeight -appBarHeight;
假设我们广告区域的高度是200,广告组件的高度一般都是固定的。
得出:广告上方距离顶部的最大距离:maxHeight= contentheight - 200;
还记得我们上面获取的dy值吗,这个值是当前广告上面距离屏幕顶部的距离,那么我们就可以得出当前广告距离AppBar底部的距离: bannerY = dy - appBarHeight - stateHeight;
同理可以得出当前广告的滑动距离:scrollY = contentheight - 200 - bannerY
;
滑动的最大距离就是:maxSrollY = contentHeight - bannerHeight
;
2、翻转
搞定了这些数据,接下来的工作就比较简单了,我们使用Transform
组件来进行180度的翻转就可以了,
获取当前滑动的比例,那就是当前滑动距离/最大滑动距离,也就是 scrollY/maxHeight;
接下来我们看下Transform
这个类,
代码:
Container(padding:EdgeInsetsDirectional.only(start:20,end:20,top:30,bottom:30),height:bannerHeight,key:_globalKey,child:Transform(alignment:Alignment.center,//相对于坐标系原点的对齐方式从中间翻转transform:Matrix4.identity()//这是一个矩阵变换类,可以对组件的坐标进行翻转,有兴趣可以了解下..rotateX(0)//翻转X轴..rotateY(angle),//翻转Y轴这里需要传入角度child:Image.asset("images/img.png",fit:BoxFit.fill,),));
通过rotateY
就可以将组件绕着Y轴进行翻转,也就达到了我们想要的3D效果,上面我们得到了滑动比例,那么我们就可以用这个比例乘以PI值
,刷新页面就可以了呗,接下来我们通过滑动监听将这个数字进行更新看下效果:
核心代码:
doubleh=MediaQuery.of(context).size.height;//屏幕高度RenderBox?renderBox=_globalKey.currentContext?.findRenderObject()asRenderBox?;double?dy=renderBox?.localToGlobal(Offset.zero).dy;//56AppBar高度if(dy!=null){//广告距离AppBarY轴距离varbannerY=dy-appBarHeight-stateHeight;//主内容区域高度varcontentHeight=h-appBarHeight-stateHeight;if(bannerY+bannerHeight<contentHeight&&bannerY>0){setState((){//滑动的距离angle=pi*((contentHeight-bannerHeight-bannerY)/(contentHeight-bannerHeight));});}}
效果:
翻转效果确实实现了,不过怎么看着有点不对劲呢,这里有两个问题:
1、划上去翻过来的图片直接镜像了。
2、当我们滑动到一半的时候,两边的宽度是一致的,3D效果不明显。
其实这两个问题都很好解决,
第一个滑动角度问题,我们滑动到90度进行翻过来的时候只需要将角度+180度进行翻转即可。这样就相当于翻了360度,最后自然会回到原来的图片的样子。
第二个我们需要设置Transform
的一个属性..setEntry(3, 2, 0.002)
,让卡片翻转过程中看起来远小近大的效果。
我们加上这两个属性再看看效果:
这样看着是不是效果就好多了。
这里我只简单了插入了一条广告,如果有多个广告建议用一个Map
对象将Key
存储起来,因为一个Key
只能对应一个组件。
完整代码
classListViewWidgetDemoextendsStatefulWidget{@overrideState<StatefulWidget>createState(){returnListViewState();}}classListViewStateextendsState<ListViewWidgetDemo>{List<NewsListBean>lis=<NewsListBean>[];lateScrollController_scrollController=ScrollController();StringimageUrl="https://images.unsplash.com/photo-1451187580459-43490279c0fa?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=60";GlobalKey_globalKey=GlobalKey();doubleangle=0;doublebannerHeight=200;@overridevoidinitState(){WidgetsBinding.instance?.addPostFrameCallback((timeStamp){_scrollController.addListener((){doubleappBarHeight=56;doublestateHeight=MediaQuery.of(context).padding.top;doubleh=MediaQuery.of(context).size.height;//屏幕高度RenderBox?renderBox=_globalKey.currentContext?.findRenderObject()asRenderBox?;double?dy=renderBox?.localToGlobal(Offset.zero).dy;//56AppBar高度if(dy!=null){//广告距离AppBarY轴距离varbannerY=dy-appBarHeight-stateHeight;//主内容区域高度varcontentHeight=h-appBarHeight-stateHeight;if(bannerY+bannerHeight<contentHeight&&bannerY>0){setState((){//滑动的距离angle=pi*((contentHeight-bannerHeight-bannerY)/(contentHeight-bannerHeight));//前半部分0-90后半部分270-360if(angle>=(pi/2)){angle=angle+pi;}});}}});});super.initState();for(inti=0;i<40;i++){lis.add(NewsListBean(i.isEven?0:1,"资讯标题$i",imageUrl,));}//插入广告lis.insert(12,NewsListBean(2,"广告",imageUrl));}@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:Text("仿网易新闻广告卡片翻转"),),body:ListView.builder(controller:_scrollController,shrinkWrap:true,scrollDirection:Axis.vertical,itemCount:lis.length,itemBuilder:(context,index){return_listWidget(lis[index]);}));}Widget_listWidget(NewsListBeanbean){lateWidgetwidget;switch(bean.type){case0:widget=Container(height:50,padding:EdgeInsetsDirectional.only(start:20),alignment:Alignment.centerLeft,color:Colors.blue[200],child:Text(bean.title,style:TextStyle(),));break;case1:widget=Row(children:[Expanded(child:Container(height:80,alignment:Alignment.center,color:Colors.red[200],margin:EdgeInsets.all(10),child:Text(bean.title)),),Image.network(bean.image,width:40,height:40,)],);break;case2:widget=Container(padding:EdgeInsetsDirectional.only(start:20,end:20,top:30,bottom:30),height:bannerHeight,key:_globalKey,child:Transform(alignment:Alignment.center,//相对于坐标系原点的对齐方式transform:Matrix4.identity()..setEntry(3,2,0.002)..rotateX(0)..rotateY(angle),child:Image.asset("images/img.png",fit:BoxFit.fill,),));break;default:widget=SizedBox();break;}returnwidget;}}classNewsListBean{//资讯类型0:资讯无图1:资讯有图2:3d广告finalinttype;finalboolisFirst;finalStringtitle;finalStringimage;NewsListBean(this.type,this.title,this.image,{this.isFirst=false});}
</div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
Flutter仿网易怎么实现广告卡片3D翻转效果的详细内容,希望对您有所帮助,信息来源于网络。