您好,登錄后才能下訂單哦!
這篇文章主要介紹“Flutter仿網(wǎng)易怎么實(shí)現(xiàn)廣告卡片3D翻轉(zhuǎn)效果”的相關(guān)知識(shí),小編通過實(shí)際案例向大家展示操作過程,操作方法簡單快捷,實(shí)用性強(qiáng),希望這篇“Flutter仿網(wǎng)易怎么實(shí)現(xiàn)廣告卡片3D翻轉(zhuǎn)效果”文章能幫助大家解決問題。
先看下網(wǎng)易新聞的效果:
看圖:
思路: 如上圖,狀態(tài)欄高度和AppBar
的高度我們都可以得到,屏幕的高度我們也可以得到,那么自然我們就可以計(jì)算出內(nèi)容區(qū)域的高度,拿到內(nèi)容區(qū)域高度我們先放到一邊,接下來我們需要獲取廣告區(qū)域距離AppBar
的距離,這是一個(gè)進(jìn)行翻轉(zhuǎn)核心數(shù)據(jù),這里我們可以通過GlobalKey
獲取這個(gè)組件的渲染對象RenderObject
并轉(zhuǎn)化為RenderBox
,通過RenderBox
我們可以獲取到這個(gè)組件在屏幕上的坐標(biāo),這樣我們拿到這個(gè)坐標(biāo)Y軸的值就是當(dāng)前組件距離頂部的距離
核心代碼:
// 這里我們獲取相對于屏幕左上角組件的坐標(biāo)y軸 GlobalKey _globalKey = GlobalKey(); RenderBox? renderBox = _globalKey.currentContext?.findRenderObject() as RenderBox?; double? dy = renderBox?.localToGlobal(Offset.zero).dy;
接下來我們就可以計(jì)算出幾個(gè)關(guān)鍵數(shù)據(jù):
狀態(tài)欄高度:stateHeight = MediaQuery.of(context).padding.top;
已知。
AppBar高度:appBarHeight = 56; 默認(rèn)高度 已知。
內(nèi)容區(qū)域高度:contentHeight = MediaQuery.of(context).size.height - stateHeight -appBarHeight;
假設(shè)我們廣告區(qū)域的高度是200,廣告組件的高度一般都是固定的。
得出:廣告上方距離頂部的最大距離:maxHeight= contentheight - 200;
還記得我們上面獲取的dy值嗎,這個(gè)值是當(dāng)前廣告上面距離屏幕頂部的距離,那么我們就可以得出當(dāng)前廣告距離AppBar底部的距離: bannerY = dy - appBarHeight - stateHeight;
同理可以得出當(dāng)前廣告的滑動(dòng)距離:scrollY = contentheight - 200 - bannerY
;
滑動(dòng)的最大距離就是:maxSrollY = contentHeight - bannerHeight
;
搞定了這些數(shù)據(jù),接下來的工作就比較簡單了,我們使用Transform
組件來進(jìn)行180度的翻轉(zhuǎn)就可以了,
獲取當(dāng)前滑動(dòng)的比例,那就是當(dāng)前滑動(dòng)距離/最大滑動(dòng)距離,也就是 scrollY/maxHeight;
接下來我們看下Transform
這個(gè)類,
代碼:
Container( padding: EdgeInsetsDirectional.only( start: 20, end: 20, top: 30, bottom: 30), height: bannerHeight, key: _globalKey, child: Transform( alignment: Alignment.center, //相對于坐標(biāo)系原點(diǎn)的對齊方式 從中間翻轉(zhuǎn) transform: Matrix4.identity()//這是一個(gè)矩陣變換類,可以對組件的坐標(biāo)進(jìn)行翻轉(zhuǎn),有興趣可以了解下 ..rotateX(0)// 翻轉(zhuǎn)X軸 ..rotateY(angle),// 翻轉(zhuǎn)Y軸 這里需要傳入角度 child: Image.asset( "images/img.png", fit: BoxFit.fill, ), ));
通過rotateY
就可以將組件繞著Y軸進(jìn)行翻轉(zhuǎn),也就達(dá)到了我們想要的3D效果,上面我們得到了滑動(dòng)比例,那么我們就可以用這個(gè)比例乘以PI值
,刷新頁面就可以了唄,接下來我們通過滑動(dòng)監(jiān)聽將這個(gè)數(shù)字進(jìn)行更新看下效果:
核心代碼:
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; // 主內(nèi)容區(qū)域高度 var contentHeight = h - appBarHeight - stateHeight; if (bannerY + bannerHeight < contentHeight && bannerY > 0) { setState(() { //滑動(dòng)的距離 angle = pi * ((contentHeight - bannerHeight - bannerY) / (contentHeight - bannerHeight)); }); } }
效果:
翻轉(zhuǎn)效果確實(shí)實(shí)現(xiàn)了,不過怎么看著有點(diǎn)不對勁呢,這里有兩個(gè)問題:
1、劃上去翻過來的圖片直接鏡像了。
2、當(dāng)我們滑動(dòng)到一半的時(shí)候,兩邊的寬度是一致的,3D效果不明顯。
其實(shí)這兩個(gè)問題都很好解決,
第一個(gè)滑動(dòng)角度問題,我們滑動(dòng)到90度進(jìn)行翻過來的時(shí)候只需要將角度+180度進(jìn)行翻轉(zhuǎn)即可。這樣就相當(dāng)于翻了360度,最后自然會(huì)回到原來的圖片的樣子。
第二個(gè)我們需要設(shè)置Transform
的一個(gè)屬性..setEntry(3, 2, 0.002)
,讓卡片翻轉(zhuǎn)過程中看起來遠(yuǎn)小近大的效果。
我們加上這兩個(gè)屬性再看看效果:
這樣看著是不是效果就好多了。
這里我只簡單了插入了一條廣告,如果有多個(gè)廣告建議用一個(gè)Map
對象將Key
存儲(chǔ)起來,因?yàn)橐粋€(gè)Key
只能對應(yīng)一個(gè)組件。
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; // 主內(nèi)容區(qū)域高度 var contentHeight = h - appBarHeight - stateHeight; if (bannerY + bannerHeight < contentHeight && bannerY > 0) { setState(() { //滑動(dòng)的距離 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, "資訊標(biāo)題$i", imageUrl, )); } // 插入廣告 lis.insert(12, NewsListBean(2, "廣告", imageUrl)); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("仿網(wǎng)易新聞廣告卡片翻轉(zhuǎn)"), ), 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, //相對于坐標(biāo)系原點(diǎn)的對齊方式 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}); }
關(guān)于“Flutter仿網(wǎng)易怎么實(shí)現(xiàn)廣告卡片3D翻轉(zhuǎn)效果”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。