您好,登錄后才能下訂單哦!
這篇文章主要介紹“Flutter仿網易怎么實現廣告卡片3D翻轉效果”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Flutter仿網易怎么實現廣告卡片3D翻轉效果”文章能幫助大家解決問題。
先看下網易新聞的效果:
看圖:
思路: 如上圖,狀態欄高度和AppBar
的高度我們都可以得到,屏幕的高度我們也可以得到,那么自然我們就可以計算出內容區域的高度,拿到內容區域高度我們先放到一邊,接下來我們需要獲取廣告區域距離AppBar
的距離,這是一個進行翻轉核心數據,這里我們可以通過GlobalKey
獲取這個組件的渲染對象RenderObject
并轉化為RenderBox
,通過RenderBox
我們可以獲取到這個組件在屏幕上的坐標,這樣我們拿到這個坐標Y軸的值就是當前組件距離頂部的距離
核心代碼:
// 這里我們獲取相對于屏幕左上角組件的坐標y軸 GlobalKey _globalKey = GlobalKey(); RenderBox? renderBox = _globalKey.currentContext?.findRenderObject() as RenderBox?; 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
;
搞定了這些數據,接下來的工作就比較簡單了,我們使用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值
,刷新頁面就可以了唄,接下來我們通過滑動監聽將這個數字進行更新看下效果:
核心代碼:
double h = MediaQuery.of(context).size.height; //屏幕高度 RenderBox? renderBox = _globalKey.currentContext?.findRenderObject() as RenderBox?; double? dy = renderBox?.localToGlobal(Offset.zero).dy; // 56 AppBar 高度 if (dy != null) { // 廣告距離AppBar Y軸距離 var bannerY = dy - appBarHeight - stateHeight; // 主內容區域高度 var contentHeight = 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
只能對應一個組件。
class ListViewWidgetDemo extends StatefulWidget { @override State<StatefulWidget> createState() { return ListViewState(); } } class ListViewState extends State<ListViewWidgetDemo> { List<NewsListBean> lis = <NewsListBean>[]; late ScrollController _scrollController = ScrollController(); String imageUrl = "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(); double angle = 0; double bannerHeight = 200; @override void initState() { WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { _scrollController.addListener(() { double appBarHeight = 56; double stateHeight = MediaQuery.of(context).padding.top; double h = MediaQuery.of(context).size.height; //屏幕高度 RenderBox? renderBox = _globalKey.currentContext?.findRenderObject() as RenderBox?; double? dy = renderBox?.localToGlobal(Offset.zero).dy; // 56 AppBar 高度 if (dy != null) { // 廣告距離AppBar Y軸距離 var bannerY = dy - appBarHeight - stateHeight; // 主內容區域高度 var contentHeight = h - appBarHeight - stateHeight; if (bannerY + bannerHeight < contentHeight && bannerY > 0) { setState(() { //滑動的距離 angle = pi * ((contentHeight - bannerHeight - bannerY) / (contentHeight - bannerHeight)); // 前半部分 0-90 后半部分 270-360 if (angle >= (pi / 2)) { angle = angle + pi; } }); } } }); }); super.initState(); for (int i = 0; i < 40; i++) { lis.add(NewsListBean( i.isEven ? 0 : 1, "資訊標題$i", imageUrl, )); } // 插入廣告 lis.insert(12, NewsListBean(2, "廣告", imageUrl)); } @override Widget build(BuildContext context) { return Scaffold( 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(NewsListBean bean) { late Widget widget; switch (bean.type) { case 0: widget = Container( height: 50, padding: EdgeInsetsDirectional.only(start: 20), alignment: Alignment.centerLeft, color: Colors.blue[200], child: Text( bean.title, style: TextStyle(), )); break; case 1: 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; case 2: 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; } return widget; } } class NewsListBean { //資訊類型 0:資訊無圖 1:資訊有圖 2:3d廣告 final int type; final bool isFirst; final String title; final String image; NewsListBean(this.type, this.title, this.image, {this.isFirst = false}); }
關于“Flutter仿網易怎么實現廣告卡片3D翻轉效果”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。