欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Flutter仿網(wǎng)易實(shí)現(xiàn)廣告卡片3D翻轉(zhuǎn)效果

 更新時(shí)間:2022年04月27日 08:19:20   作者:老李code  
在逛網(wǎng)易新聞時(shí),發(fā)現(xiàn)列表中的廣告在你滑動(dòng)的時(shí)候會(huì)有一個(gè)3D旋轉(zhuǎn)的交互引你的注意。本文將利用Flutter實(shí)現(xiàn)這一效果,感興趣的可以了解一下

前言

在逛網(wǎng)易新聞時(shí),發(fā)現(xiàn)列表中的廣告在你滑動(dòng)的時(shí)候會(huì)有一個(gè)3D旋轉(zhuǎn)的交互引你的注意,不得不說這些產(chǎn)品為了讓用戶看廣告花樣百出,那么今天我們就用Flutter也實(shí)現(xiàn)這么一個(gè)效果。

先看下網(wǎng)易新聞的效果:

OK,先說了我看到這個(gè)效果的思路:首先我們看到這個(gè)廣告卡片在從底部向上滑的時(shí)候在完全滑入到顯示屏區(qū)域內(nèi)開始3D旋轉(zhuǎn),到這個(gè)卡片頂部到達(dá)列表頂部時(shí)翻轉(zhuǎn)結(jié)束,那我們主要還是需要計(jì)算這個(gè)廣告卡片距離列表底部的距離和距離列表頂部的距離,有了這兩個(gè)距離,那么我們就可以根據(jù)Transform進(jìn)行Y軸翻轉(zhuǎn)180°就好了。

實(shí)現(xiàn)思路

1、獲取各種距離

看圖:

思路: 如上圖,狀態(tài)欄高度和AppBar的高度我們都可以得到,屏幕的高度我們也可以得到,那么自然我們就可以計(jì)算出內(nèi)容區(qū)域的高度,拿到內(nèi)容區(qū)域高度我們先放到一邊,接下來我們需要獲取廣告區(qū)域距離AppBar的距離,這是一個(gè)進(jìn)行翻轉(zhuǎn)核心數(shù)據(jù),這里我們可以通過GlobalKey獲取這個(gè)組件的渲染對(duì)象RenderObject并轉(zhuǎn)化為RenderBox,通過RenderBox我們可以獲取到這個(gè)組件在屏幕上的坐標(biāo),這樣我們拿到這個(gè)坐標(biāo)Y軸的值就是當(dāng)前組件距離頂部的距離

核心代碼:

// 這里我們獲取相對(duì)于屏幕左上角組件的坐標(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;

2、翻轉(zhuǎn)

搞定了這些數(shù)據(jù),接下來的工作就比較簡(jiǎn)單了,我們使用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, //相對(duì)于坐標(biāo)系原點(diǎn)的對(duì)齊方式 從中間翻轉(zhuǎn)
      transform: Matrix4.identity()//這是一個(gè)矩陣變換類,可以對(duì)組件的坐標(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值,刷新頁(yè)面就可以了唄,接下來我們通過滑動(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)不對(duì)勁呢,這里有兩個(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è)屬性再看看效果:

這樣看著是不是效果就好多了。

這里我只簡(jiǎn)單了插入了一條廣告,如果有多個(gè)廣告建議用一個(gè)Map對(duì)象將Key存儲(chǔ)起來,因?yàn)橐粋€(gè)Key只能對(duì)應(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, //相對(duì)于坐標(biāo)系原點(diǎn)的對(duì)齊方式
              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:資訊無(wú)圖 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});
}

小結(jié)

通過本篇文章我們可以學(xué)習(xí)到,獲取組件的坐標(biāo)以及3D翻轉(zhuǎn)的效果,實(shí)現(xiàn)這種效果可能也有其他更好的方式,本篇文章只提供了一個(gè)實(shí)現(xiàn)思路。

到此這篇關(guān)于Flutter仿網(wǎng)易實(shí)現(xiàn)廣告卡片3D翻轉(zhuǎn)效果的文章就介紹到這了,更多相關(guān)Flutter廣告卡片翻轉(zhuǎn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入理解Android之接口回調(diào)機(jī)制

    深入理解Android之接口回調(diào)機(jī)制

    本篇文章主要介紹了Android之接口回調(diào)機(jī)制,在開發(fā)中經(jīng)常會(huì)用到,具有一定的學(xué)習(xí)價(jià)值,有需要的可以來了解一下。
    2016-10-10
  • 正確在Flutter中添加webview實(shí)現(xiàn)詳解

    正確在Flutter中添加webview實(shí)現(xiàn)詳解

    這篇文章主要為大家介紹了正確在Flutter中添加webview實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Android 自定義 View 中使用 Spannable的實(shí)例詳解

    Android 自定義 View 中使用 Spannable的實(shí)例詳解

    這篇文章主要介紹了Android 自定義 View 中使用 Spannable的相關(guān)知識(shí),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-05-05
  • 總結(jié)Android App內(nèi)存優(yōu)化之圖片優(yōu)化

    總結(jié)Android App內(nèi)存優(yōu)化之圖片優(yōu)化

    網(wǎng)上有很多大拿分享的關(guān)于Android性能優(yōu)化的文章,主要是通過各種工具分析,使用合理的技巧優(yōu)化APP的體驗(yàn),提升APP的流暢度,但關(guān)于內(nèi)存優(yōu)化的文章很少有看到。下面是我在實(shí)踐過程中使用的一些方法,很多都是不太成熟的項(xiàng)目,只是將其作為一種處理方式分享給大家。
    2016-08-08
  • Android Room數(shù)據(jù)庫(kù)容易遇到的問題以及解決方法

    Android Room數(shù)據(jù)庫(kù)容易遇到的問題以及解決方法

    這篇文章給大家介紹了我們?cè)贏ndroid Room數(shù)據(jù)庫(kù)容易遇到的坑以及解決方法,文中有詳細(xì)的代碼示例供我們參考,具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-09-09
  • Flutter?EventBus事件總線的應(yīng)用詳解

    Flutter?EventBus事件總線的應(yīng)用詳解

    這篇文章主要為大家介紹了Flutter?EventBus事件總線的應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Android apk 插件啟動(dòng)內(nèi)存釋放問題

    Android apk 插件啟動(dòng)內(nèi)存釋放問題

    這篇文章主要介紹了Android apk 插件啟動(dòng)內(nèi)存釋放問題的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • 教你五分鐘實(shí)現(xiàn)Android超漂亮的刻度輪播控件實(shí)例教程

    教你五分鐘實(shí)現(xiàn)Android超漂亮的刻度輪播控件實(shí)例教程

    說到輪播圖,想必大家都不陌生,下面這篇文章主要給大家介紹了關(guān)于如何利用五分鐘快速實(shí)現(xiàn)一款超漂亮的Android刻度輪播控件的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧
    2018-09-09
  • Android Studio項(xiàng)目適配AndroidX(Android 9.0)的方法步驟

    Android Studio項(xiàng)目適配AndroidX(Android 9.0)的方法步驟

    這篇文章主要介紹了Android Studio項(xiàng)目適配AndroidX(Android 9.0)的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • 另外兩種Android沉浸式狀態(tài)欄實(shí)現(xiàn)思路

    另外兩種Android沉浸式狀態(tài)欄實(shí)現(xiàn)思路

    這篇文章主要為大家介紹了另外兩種Android沉浸式狀態(tài)欄實(shí)現(xiàn)思路,android5.0及以后版本都支持給狀態(tài)欄著色,而目前android主流版本還是4.4,想要深入了解的朋友可以參考一下
    2016-01-01

最新評(píng)論