Android Flutter实现3D动画效果示例详解
作者:岛上码农 发布时间:2022-04-12 19:34:35
前言
上一篇我们介绍了 Animation
和 AnimationController
的使用,这是最基本的动画构建类。但是,如果我们想构建一个可复用的动画组件,通过外部参数来控制其动画效果的时候,上一篇的方法就不太合适了。在 Flutter 中提供了 AnimatedWidget
组件用于构建可复用的动画组件。本篇我们用 AnimatedWidget
来实现组件的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),分别是:
transform
:Matrix4
对象,可以实现围绕 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,
),
),
//...
图片旋转.gif
来源:https://mp.weixin.qq.com/s/944KqcX3uoDdB3Q18VbrRw


猜你喜欢
- 选择排序选择排序是一种简单直观的排序算法,它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未
- Android 3.0引入了CursorLoader实现异步加载数据,为了避免同步查询数据库时阻塞UI线程的问题。在API 11之前可以通过
- 在很多的Android项目中都需要用户登录、注册。这样的话在开发中做好保护用户密码的工作就显得尤为重要。这里我把自己的密码保护方法记录下来。
- springMVC的生命周期,听到的时候都没有反应过来,springMVC还有生命周期?现在看来生命周期就是springMVC的流程,Spr
- 页面报错:后台错误:Field error in object 'user' on field 'birthday&
- 跨域配置如下,Springboot 版本为 2.4.1///跨域访问配置@Configurationpublic class CorsCon
- 本文实例为大家分享了android利用handler实现倒计时的具体代码,供大家参考,具体内容如下xml<?xml version=&
- 什么是有序性在开发中,我们通常按照从上到下的顺序编写程序指令,并且希望cpu和编译器按照我们预先编写的顺序去执。但往往cpu和编译器为了提高
- 方式一:通过java.net.InetAddress类获取public void test1() { try { InetAdd
- 抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。下面我们以水果为例,首先定
- 本文实例为大家分享了android实现简单仪表盘效果的具体代码,供大家参考,具体内容如下实现这个效果:中间的文字很好写,外层的进度条就需要自
- 本文实例讲述了Java单例模式。分享给大家供大家参考,具体如下:在实际开发的时候会有一些需求,在某个类中只能允许同时存在一个对象。这时就需要
- 前言:在我们使用C# WinForm中,我们有时候是需要或者自己本机的IP地址进行处理,今天我们学习一下如何使用C# Winform获取主机
- Java Set集合的遍历及实现类的比较Java中Set集合是一个不包含重复元素的Collection,首先我们先看看遍历方法package
- 同一个service调用service本身如果同一个service调用service本身的方法,出现了事务不能控制。解决方案1.在sprin
- 前言解放双手!推荐一款阿里开源的低代码工具,YYDS!之前分享过一些低代码相关的文章,发现大家还是比较感兴趣的。之前在我印象中低代码就是通过
- 初看 cgaolei 翻译的 Java技巧之双括弧初始化 一文,走马观花,只知用法,未细看后面的解释。蔚为惊艳,心里想 Java 竟然有这么
- Mutex类、Event类、SemaphoreSlim类和ReaderWriterLockSlim类等提供了多个进程之间的线程同步。&nbs
- 本文实例讲述了Android编程中context及全局变量的用法。分享给大家供大家参考,具体如下:今天在研究context的时候,对appl
- 本文实例讲述了Android编程开发之Spinner组件用法。分享给大家供大家参考,具体如下:Spinner组件组要用显示一个下拉列表,在使