最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

前言

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

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

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

實現(xiàn)思路

1、獲取各種距離

看圖:

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

核心代碼:

// 這里我們獲取相對于屏幕左上角組件的坐標y軸

GlobalKey _globalKey = GlobalKey();

RenderBox? renderBox =
    _globalKey.currentContext?.findRenderObject() as RenderBox?;
double? dy = renderBox?.localToGlobal(Offset.zero).dy;

接下來我們就可以計算出幾個關(guān)鍵數(shù)據(jù):

狀態(tài)欄高度:stateHeight = MediaQuery.of(context).padding.top;已知。

AppBar高度:appBarHeight = 56; 默認高度 已知。

內(nèi)容區(qū)域高度:contentHeight = MediaQuery.of(context).size.height - stateHeight -appBarHeight;

假設(shè)我們廣告區(qū)域的高度是200,廣告組件的高度一般都是固定的。

得出:廣告上方距離頂部的最大距離:maxHeight= contentheight - 200;

還記得我們上面獲取的dy值嗎,這個值是當前廣告上面距離屏幕頂部的距離,那么我們就可以得出當前廣告距離AppBar底部的距離: bannerY = dy - appBarHeight - stateHeight;

同理可以得出當前廣告的滑動距離:scrollY = contentheight - 200 - bannerY;

滑動的最大距離就是:maxSrollY = contentHeight - bannerHeight;

2、翻轉(zhuǎn)

搞定了這些數(shù)據(jù),接下來的工作就比較簡單了,我們使用Transform組件來進行180度的翻轉(zhuǎn)就可以了,
獲取當前滑動的比例,那就是當前滑動距離/最大滑動距離,也就是 scrollY/maxHeight; 接下來我們看下Transform這個類,

代碼:

Container(
    padding: EdgeInsetsDirectional.only(
        start: 20, end: 20, top: 30, bottom: 30),
    height: bannerHeight,
    key: _globalKey,
    child: Transform(
      alignment: Alignment.center, //相對于坐標系原點的對齊方式 從中間翻轉(zhuǎn)
      transform: Matrix4.identity()//這是一個矩陣變換類,可以對組件的坐標進行翻轉(zhuǎn),有興趣可以了解下
        ..rotateX(0)// 翻轉(zhuǎn)X軸
        ..rotateY(angle),// 翻轉(zhuǎn)Y軸 這里需要傳入角度
      child: Image.asset(
        "images/img.png",
        fit: BoxFit.fill,
      ),
    ));

通過rotateY就可以將組件繞著Y軸進行翻轉(zhuǎn),也就達到了我們想要的3D效果,上面我們得到了滑動比例,那么我們就可以用這個比例乘以PI值,刷新頁面就可以了唄,接下來我們通過滑動監(jiān)聽將這個數(shù)字進行更新看下效果:

核心代碼:

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(() {
      //滑動的距離
      angle = pi * ((contentHeight - bannerHeight - bannerY) /
              (contentHeight - bannerHeight));
     
    });
  }
}

效果:

翻轉(zhuǎn)效果確實實現(xiàn)了,不過怎么看著有點不對勁呢,這里有兩個問題:

1、劃上去翻過來的圖片直接鏡像了。

2、當我們滑動到一半的時候,兩邊的寬度是一致的,3D效果不明顯。

其實這兩個問題都很好解決,

第一個滑動角度問題,我們滑動到90度進行翻過來的時候只需要將角度+180度進行翻轉(zhuǎn)即可。這樣就相當于翻了360度,最后自然會回到原來的圖片的樣子。

第二個我們需要設(shè)置Transform的一個屬性..setEntry(3, 2, 0.002),讓卡片翻轉(zhuǎn)過程中看起來遠小近大的效果。

我們加上這兩個屬性再看看效果:

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

這里我只簡單了插入了一條廣告,如果有多個廣告建議用一個Map對象將Key存儲起來,因為一個Key只能對應(yīng)一個組件。

完整代碼

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(() {
              //滑動的距離
              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("仿網(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, //相對于坐標系原點的對齊方式
              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});
}

小結(jié)

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

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

相關(guān)文章

最新評論

镶黄旗| 尤溪县| 尉氏县| 宽甸| 盐山县| 巧家县| 大埔县| 会同县| 长岭县| 阳新县| 利辛县| 邹城市| 吴桥县| 自治县| 屯留县| 大连市| 龙胜| 利辛县| 广德县| 东山县| 镇坪县| 且末县| 柏乡县| 博湖县| 瑞丽市| SHOW| 孟州市| 扶绥县| 宜昌市| 九龙坡区| 晋中市| 名山县| 宝坻区| 莫力| 华安县| 瑞昌市| 土默特右旗| 会宁县| 永宁县| 思南县| 苏尼特右旗|