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


Posted in Java/Android onApril 07, 2022

前言

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

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

AnimatedWidget 简介

AnimatedWidget是一个抽象的 StatefulWidget, 构造方法如下所示。

const AnimatedWidget({
    Key? key,
    required this.listenable,
  }) : assert(listenable != null),
       super(key: key);

主要在于接收一个 listenable 参数,通常会是 Animation 对象。在 AnimatedWidget 内部的_AnimatedState 类中,会添加该对象变化监听回调,进而刷新界面。

class _AnimatedState extends State<AnimatedWidget> {
  @override
  void initState() {
    super.initState();
    widget.listenable.addListener(_handleChange);
  }

  @override
  void didUpdateWidget(AnimatedWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.listenable != oldWidget.listenable) {
      oldWidget.listenable.removeListener(_handleChange);
      widget.listenable.addListener(_handleChange);
    }
  }

  @override
  void dispose() {
    widget.listenable.removeListener(_handleChange);
    super.dispose();
  }

  void _handleChange() {
    setState(() {
      // The listenable's state is our build state, and it changed already.
    });
  }
  
  // ...
}

可以看到,只需要将 Animation 对象传给 AnimatedWidget 对象后,就不需要我们之前那样自己写 addListener 之类的处理了。而整个动画可以交给外部其他对象来控制,从而实现动画组件的复用。

3D 旋转动画的实现

3D 旋转的实现比较简单,在 Container 组件有两个参数控制转换(transform),分别是:

  • transformMatrix4对象,可以实现围绕 X、Y、Z轴的旋转、平移,以及变形等效果。关于 Matrix4涉及到很多矩阵运算和线性代数的知识,可以参考 Matrix4的源码自行温习一下大学的数学知识。
  • transformAlignment:转换的对齐方式,可以理解为起点位置,可以使用 Alignment 对象来设置。

有了这个基础,我们就可以定义3D 旋转动效了,我们定义一个通用的组件,ThreeDAnimatedWidget

class ThreeDAnimatedWidget extends AnimatedWidget {
  final Widget child;
  const ThreeDAnimatedWidget(
      {Key? key, required Animation<double> animation, required this.child})
      : super(key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    final animation = listenable as Animation<double>;

    return Center(
      child: Container(
        transform: Matrix4.identity()
          ..rotateY(2 * pi * animation.value)
          ..setEntry(1, 0, 0.01),
        transformAlignment: Alignment.center,
        child: child,
      ),
    );
  }
}

这里我们设置的是围绕中心点绕 Y 轴旋转,并使用 setEntry 设置了一定的倾斜角 (这会更有立体感)。实际我们也可以设置围绕 X 轴或 Z 轴旋转。接下来就是这个动画组件的应用了,我们构建一个带有阴影的文字(看起来像立体字)作为这个动画的子组件,其他的控制和上一篇的是类似的,完整代码如下:

class AnimatedWidgetDemo extends StatefulWidget {
  const AnimatedWidgetDemo({Key? key}) : super(key: key);

  @override
  _AnimatedWidgetDemoState createState() => _AnimatedWidgetDemoState();
}

class _AnimatedWidgetDemoState extends State<AnimatedWidgetDemo>
    with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller =
        AnimationController(duration: const Duration(seconds: 3), vsync: this);
    animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AnimatedWidget 动画'),
      ),
      body: ThreeDAnimatedWidget(
        animation: animation,
        child: Text(
          '岛上码农',
          style: TextStyle(
            fontSize: 42.0,
            color: Colors.blue,
            fontWeight: FontWeight.bold,
            shadows: [
              Shadow(
                  blurRadius: 2,
                  offset: Offset(2.0, 1.0),
                  color: Colors.blue[900]!),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow, color: Colors.white),
        onPressed: () {
          if (controller.status == AnimationStatus.completed) {
            controller.reverse();
          } else {
            controller.forward();
          }
        },
      ),
    );
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

可以看到,这个 ThreeDAnimatedWidget 可以做到复用了,在需要这样动效的场景里,按照上面的方式给它传入 Animation 对象和子组件就可以了。例如我们将文字修改为一张图片。

//...
body: ThreeDAnimatedWidget(
  animation: animation,
  child: Image.asset(
    'images/avatar.jpg',
    width: 100,
    height: 100,
  ),
),
//...

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

图片旋转.gif

总结

本篇介绍了 AnimatedWidget 的使用,通过 AnimatedWidget 可以构建可复用的动画组件。同时,还通过 Container 的 transform 属性加上 AnimatedWidget 实现了三维空间的旋转效果。实际开发过程中,我们可以基于 AnimatedWidget 构建很多个性化的、可复用的动画组件,提升应用的趣味性。

以上就是Android Flutter实现3D动画效果示例详解的详细内容,更多关于Flutter 3D动画的资料请关注三水点靠木其它相关文章!

Java/Android 相关文章推荐
Java方法重载和方法重写的区别到底在哪?
Jun 11 Java/Android
教你用Java在个人电脑上实现微信扫码支付
Jun 13 Java/Android
SpringCloud的JPA连接PostgreSql的教程
Jun 26 Java/Android
IDEA2021.2配置docker如何将springboot项目打成镜像一键发布部署
Sep 25 Java/Android
SpringBoot+Redis实现布隆过滤器的示例代码
Mar 17 Java/Android
springboot+zookeeper实现分布式锁
Mar 21 Java/Android
Flutter集成高德地图并添加自定义Maker的实践
Apr 07 Java/Android
引用计数法和root搜索算法以及JVM中判定对象需要回收的方法
Apr 19 Java/Android
Mybatis-plus配置分页插件返回统一结果集
Jun 21 Java/Android
maven 解包依赖项中的文件的解决方法
Jul 15 Java/Android
向Spring IOC 容器动态注册bean实现方式
Jul 15 Java/Android
Android Flutter实现图片滑动切换效果
MyBatis配置文件解析与MyBatis实例演示
Java 深入探究讲解简单工厂模式
springboot用户数据修改的详细实现
Apr 06 #Java/Android
Java中API的使用方法详情
Java 获取Word中所有的插入和删除修订的方法
springboot应用服务启动事件的监听实现
Apr 06 #Java/Android
You might like
来自PHP.NET的入门教程
2006/10/09 PHP
PHP在不同页面间传递Json数据示例代码
2013/06/08 PHP
php数组比较实现查找连续数的方法
2015/07/29 PHP
关于PHP中协程和阻塞的一些理解与思考
2017/08/11 PHP
laravel ORM关联关系中的 with和whereHas用法
2019/10/16 PHP
PHP filter_var() 函数, 验证判断EMAIL,URL等
2021/03/09 PHP
使用IE6看老赵的博客 jQuery初探
2010/01/17 Javascript
jquery.jstree 增加节点的双击事件代码
2010/07/27 Javascript
JS 加入收藏夹的代码(主流浏览器通用)
2013/05/13 Javascript
解决Extjs4中form表单提交后无法进入success函数问题
2013/11/26 Javascript
javascript创建数组之联合数组的使用方法示例
2013/12/26 Javascript
JavaScript二维数组实现的省市联动菜单
2014/05/08 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之绑定事件
2015/11/19 Javascript
Bootstrap开发实战之第一次接触Bootstrap
2016/06/02 Javascript
关于vue-resource报错450的解决方案
2017/07/24 Javascript
使用cookie绕过验证码登录的实现代码
2017/10/12 Javascript
JavaScript 有用的代码片段和 trick
2018/02/22 Javascript
element ui里dialog关闭后清除验证条件方法
2018/02/26 Javascript
Vue中的无限加载vue-infinite-loading的方法
2018/04/08 Javascript
JS获取指定月份的天数两种实现方法
2018/06/22 Javascript
javacript replace 正则取字符串中的值并替换【推荐】
2018/09/13 Javascript
详解webpack打包第三方类库的正确姿势
2018/10/20 Javascript
vscode 开发Vue项目的方法步骤
2018/11/25 Javascript
vue-dplayer 视频播放器实例代码
2019/11/08 Javascript
使用JavaScript实现贪吃蛇游戏
2020/09/29 Javascript
Python中使用装饰器时需要注意的一些问题
2015/05/11 Python
使用python进行文本预处理和提取特征的实例
2018/06/05 Python
使用python来调用CAN通讯的DLL实现方法
2019/07/03 Python
django 多对多表的创建和插入代码实现
2019/09/09 Python
Python pandas库中的isnull()详解
2019/12/26 Python
Python基于pyecharts实现关联图绘制
2020/03/27 Python
解决Python Matplotlib绘图数据点位置错乱问题
2020/05/16 Python
英国美发和美容产品商城:HQhair
2019/02/08 全球购物
平安校园建设方案
2014/05/02 职场文书
个人对照检查材料思想汇报
2014/09/26 职场文书
python中出现invalid syntax报错的几种原因分析
2022/02/12 Python