Flutter仿网易实现广告卡片3D翻转效果

目录
  • 前言
  • 实现思路
    • 1、获取各种距离
    • 2、翻转
  • 完整代码
  • 小结

前言

在逛网易新闻时,发现列表中的广告在你滑动的时候会有一个3D旋转的交互引你的注意,不得不说这些产品为了让用户看广告花样百出,那么今天我们就用Flutter也实现这么一个效果。

先看下网易新闻的效果:

OK,先说了我看到这个效果的思路:首先我们看到这个广告卡片在从底部向上滑的时候在完全滑入到显示屏区域内开始3D旋转,到这个卡片顶部到达列表顶部时翻转结束,那我们主要还是需要计算这个广告卡片距离列表底部的距离和距离列表顶部的距离,有了这两个距离,那么我们就可以根据Transform进行Y轴翻转180°就好了。

实现思路

1、获取各种距离

看图:

思路: 如上图,状态栏高度和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;

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值,刷新页面就可以了呗,接下来我们通过滑动监听将这个数字进行更新看下效果:

核心代码:

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});
}

小结

通过本篇文章我们可以学习到,获取组件的坐标以及3D翻转的效果,实现这种效果可能也有其他更好的方式,本篇文章只提供了一个实现思路。

到此这篇关于Flutter仿网易实现广告卡片3D翻转效果的文章就介绍到这了,更多相关Flutter广告卡片翻转内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 基于Flutter制作一个心碎动画特效

    目录 前言 实现步骤 1.绘制一个心 2.绘制心的裂痕 3.加入动画 完整代码 小结 前言 继续动画探索,今天用Flutter制作一个心碎的感觉,灵感来源于今天的股市,哎,心哇凉哇凉的.废话不多说,开始. 效果图先上: 实现步骤 1.绘制一个心 首先我们使用两段三阶贝塞尔曲线制作一个心型,这里因为需要实现心碎的效果,所以我们需要将心的两段用两段路径path进行绘制出来,效果: 绘制代码: canvas.translate(size.width / 2, size.height / 2); Pai

  • 基于Flutter制作一个吃豆人加载动画

    目录 效果图 绘制静态吃豆人.豆豆.眼睛 加入动画属性 总结 效果图 国际惯例,先看效果图: 具体效果就是吃豆人会根据吃不同颜色的豆子改变身体的颜色. 绘制静态吃豆人.豆豆.眼睛 首先,我们需要将这个静态的吃豆人绘制出来,我们可以把吃豆人看做是一个实心圆弧,豆豆和眼睛就是一个圆. 关键代码: //画头 _paint ..color = color.value ..style = PaintingStyle.fill; var rect = Rect.fromCenter( center: Off

  • 基于Flutter实现爱心三连动画效果

    目录 前言 Animation 简介 AnimationController 简介 应用 - 爱心三连 总结 前言 我们开始 Flutter 动画相关篇章之旅,在应用中通过动效能够给用户带来更愉悦的体验,比较典型的例子就是一些直播平台的动效了,比如送火箭能做出来那种火箭发射的动效——感觉倍有面子,当然这是土豪的享受,我等码农只在视频里看过.本篇我们来介绍基于 Animation 类实现的基本动画构建. Animation 简介 Animation 是一个抽象类,它并不参与屏幕的绘制,而是在设定的

  • 在Flutter中制作翻转卡片动画的完整实例代码

    目录 前言 使用自写代码 预览 完整代码 使用第三个插件 编码 结论 前言 本文将带您了解在 Flutter 中制作翻转卡片动画的两个完整示例.第一个示例从头开始实现,第二个示例使用第三方包.闲话少说,让我们动手吧. 使用自写代码 本示例使用变换小部件创建翻转卡片效果. 预览 我们将要构建的演示应用程序显示了两张隐藏一些秘密的卡片.您可以通过按"揭示秘密" 按钮来揭开面具背后的东西.最上面的卡片展示了一个水平翻转动画,底部一张展示了一个垂直翻转动画. 完整代码 // main.dart

  • Android Flutter实现3D动画效果示例详解

    目录 前言 AnimatedWidget 简介 3D 旋转动画的实现 总结 前言 上一篇我们介绍了 Animation 和 AnimationController 的使用,这是最基本的动画构建类.但是,如果我们想构建一个可复用的动画组件,通过外部参数来控制其动画效果的时候,上一篇的方法就不太合适了.在 Flutter 中提供了 AnimatedWidget 组件用于构建可复用的动画组件.本篇我们用 AnimatedWidget 来实现组件的3D 旋转效果,如下图所示. AnimatedWidge

  • Flutter实现牛顿摆动画效果的示例代码

    目录 前言 实现步骤 1.绘制静态效果 2.加入动画 两个关键点 完整源码 总结 前言 牛顿摆大家应该都不陌生,也叫碰碰球.永动球(理论情况下),那么今天我们用Flutter实现这么一个理论中的永动球,可以作为加载Loading使用. - 知识点:绘制.动画曲线.多动画状态更新 效果图: 实现步骤 1.绘制静态效果 首先我们需要把线和小圆球绘制出来,对于看过我之前文章的小伙伴来说这个就很简单了,效果图: 关键代码: // 小圆球半径 double radius = 6; /// 小球圆心和直线终

  • Flutter仿网易实现广告卡片3D翻转效果

    目录 前言 实现思路 1.获取各种距离 2.翻转 完整代码 小结 前言 在逛网易新闻时,发现列表中的广告在你滑动的时候会有一个3D旋转的交互引你的注意,不得不说这些产品为了让用户看广告花样百出,那么今天我们就用Flutter也实现这么一个效果. 先看下网易新闻的效果: OK,先说了我看到这个效果的思路:首先我们看到这个广告卡片在从底部向上滑的时候在完全滑入到显示屏区域内开始3D旋转,到这个卡片顶部到达列表顶部时翻转结束,那我们主要还是需要计算这个广告卡片距离列表底部的距离和距离列表顶部的距离,有

  • Android使用animator实现fragment的3D翻转效果

    今天老师留的作业,使用俩个Fragment来实现3D翻转效果,遇到了一点点的问题,于是在网上进行了查找,但是发现有些博主的代码不正确,对其他人进行了误导,在网上使用属性动画实现3D效果非常少,所以经过我自己的实验摸索,我将自己的代码和遇到的问题给他讲解一下提供一点点借鉴,并且希望可以帮助到大家. 首先讲解一下主要实现动画的函数: getFragmentManager().beginTransaction() .setCustomAnimations(R.animator.fragment_sec

  • Android动画之3D翻转效果实现函数分析

    Android中的翻转动画效果的实现,首先看一下运行效果如上图所示. Android中并没有提供直接做3D翻转的动画,所以关于3D翻转的动画效果需要我们自己实现,那么我们首先来分析一下Animation 和 Transformation. Animation动画的主要接口,其中主要定义了动画的一些属性比如开始时间,持续时间,是否重复播放等等.而Transformation中则包含一个矩阵和alpha值,矩阵是用来做平移,旋转和缩放动画的,而alpha值是用来做alpha动画的,要实现3D旋转动画

  • javafx实现图片3D翻转效果方法实例

    实现步骤: 1.定义FlipView对象.包含以下属性: 复制代码 代码如下: //正面视图 public Node frontNode; //反面视图 public Node backNode; //是否翻转 boolean flipped = false; //翻转角度 DoubleProperty time = new SimpleDoubleProperty(Math.PI / 2); //正面翻转特效 PerspectiveTransform frontEffect = new Per

  • jQuery实现仿路边灯箱广告图片轮播效果

    特效介绍 本图片幻灯就像路边灯箱广告,路边大广告牌效果,LED切换效果,并且会一直保持在页面最低端. 演示图 使用方法 1.在head区域引入style.css. 复制代码 代码如下: <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8"/> 2.html代码放在</body>上面: <div cla

  • Android仿网易严选底部弹出菜单效果

    在网易严选的看东西的时候在商品详情页里看到他的底部弹出菜单,本能反应是想用DottomSheetDialog或者PopupWindow来实现,可是发现实现不了他那种效果,于是就自己模仿一个像严选这样的底部弹出菜单. 不管是DottomSheetDialog或者PopupWindow他们的阴影背景都是全部覆盖的,这就造成除了菜单内容的View之外其他都是阴影的,而严选不是这样的.唠叨到此,首先展示效果图如下: 是不是还可以呢,由于代码量不多却注释详细,所以先贴出代码再一一详说: BottomPop

  • Android实现3D翻转动画效果

    Android中并没有提供直接做3D翻转的动画,所以关于3D翻转的动画效果需要我们自己实现,那么我们首先来分析一下Animation 和 Transformation. Animation动画的主要接口,其中主要定义了动画的一些属性比如开始时间,持续时间,是否重复播放等等.而Transformation中则包含一个矩阵和alpha值,矩阵是用来做平移,旋转和缩放动画的,而alpha值是用来做alpha动画的,要实现3D旋转动画我们需要继承自Animation类来实现,我们需要重载getTrans

  • Android实现dialog的3D翻转示例

    本文实现了Android中dialog的3D翻转效果.这里通过一个简单的应用场景记录下. 效果图 起初自己的思路是Activity进行界面跳转实现旋转效果,网上看了很多,写下来发现效果不对.之后又看到Google上面的Card Flid Animation效果是这样的. 看着确实不错,然而拿下来demo放慢翻转速度后发现,不是我想要的.但是跟我看到的一个app里面的效果一样 然后想改成dialog试试效果,发现更是不行了. Card Flid Animation效果如下: 这个是通过Activi

  • JS中利用swiper实现3d翻转幻灯片实例代码

    前言 Swiper是纯javascript打造的滑动特效插件,面向手机.平板电脑等移动终端.Swiper能实现触屏焦点图.触屏Tab切换.触屏多图切换等常用效果.Swiper开源.免费.稳定.使用简单.功能强大,是架构移动终端网站的重要选择! 本文详细的给大家介绍了关于JS用swiper实现3d翻转幻灯片的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 先上效果图 使用的是swiper3.0版本coverflow效果,源码如下 <!DOCTYPE html> <

  • JS实现六边形3D拖拽翻转效果的方法

    效果图 实例代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv=&q

随机推荐