前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >《Flutter》-- 8.动画

《Flutter》-- 8.动画

作者头像
爱学习的程序媛
发布于 2022-04-07 08:33:25
发布于 2022-04-07 08:33:25
1.2K00
代码可运行
举报
文章被收录于专栏:学习/读书笔记学习/读书笔记
运行总次数:0
代码可运行

参阅书籍:

Flutter跨平台开发入门与实践》-- 向治洪(著)

8. 动画

8.1 动画基础

不管是什么视图框架,动画的实现原理都是相同的,即在一段时限的时间内,多次、快速地改变视图外观来实现连续播放的效果。

视图的一次改变称为一个动画帧,对应一次屏幕刷新,决定动画流畅度的一个重要指标就是帧率(Frame Per Second,FPS),即每秒的动画帧数。

对于人眼,动画帧率超过16FPS就认为是流畅的,超过32FPS基本就感受不到任何卡顿,为了保证良好的视觉体验需要帧率尽可能达到60FPS。

Flutter框架是可以实现60FPS的,这和原生应用的帧率标准是基本持平的。

8.1.1 Animation

Animation是一个Flutter动画中的核心抽象类,主要用于保存动画的插值和状态,它本身与视图渲染没有任何关系。

Animation对象的状态有4种:

1)dismissed:动画处于开始状态;

2)forward:动画正在正向执行;

3)reverse:动画正在反向执行;

4)completed:动画处于结束状态。

Animation对象有Listeners和StatusListeners两个监听器两个监听器,可以用来监听动画的变化。

如果需要监听动画每一帧以及执行状态的变化,可以使用addListener()和addStatusListener()。

addListener()用于给Animation对象添加帧监听器,每一帧都会被调用,当帧监听器监听到状态发生改变后就会调用setState()来触发视图的重建。

addStatusListener()用于给Animation对象添加动画状态改变监听器,动画开始、结束、正向或反向时就会调用状态改变的监听器。

8.1.2 AnimationController

AnimationController表示动画控制器,是一个特殊的Animation对象,主要用于控制动画的开始、结束、正向、反向等操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
AnimationController controller = AnimationController (
  duration: const Duration(milliseconds: 2000),
  lowerBound: 10.0,
  upperBound: 20.0,
  vsync: this,//接收一个TickerProvider类型的对象,防止屏幕锁屏后继续执行动画造成资源浪费
)

使用AnimationController 创建的Animation动画对象,默认情况下不会启动,要让动画运行,需要调用AnimationController 的forward()方法。

TickerProvider是一个抽象类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
abstract class TickerProvider {
  Ticker createTicker(TickerCallback onTick);
}

Flutter应用在启动时会绑定一个SchedulerBinding,通过SchedulerBinding可以给每一次屏幕刷新添加回调,而Ticker对象就是通过SchedulerBinding来实现屏幕刷新回调的,每次屏幕刷新都会调用TickerCallback 。

在Flutter动画中,使用Ticker而不是Timer来驱动动画,可以有效防止屏幕外动画(如锁屏)带来的资源消耗。Flutter在屏幕刷新时会通知绑定的SchedulerBinding,而Ticker是受SchedulerBinding驱动的,锁屏后屏幕停止刷新,Ticker也就不会再被触发。

AnimationController 常用函数:

1)forward():开始播放动画;

2)stop():停止动画播放;

3)reset():重置动画为初始化状态;

4)reverse():反向动画播放,必须正向动画播放完成后才有效;

5)repeat():循环播放动画;

6)dispose():销毁动画,释放动画占用资源。

8.1.3 Curve

Curve主要用来控制动画随时间的变化率,默认为均匀的线性变化。

在Flutter应用开发中,可以通过CurvedAnimation来指定动画的曲线:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CurvedAnimation curve = CurvedAnimation (
  parent: controller,
  curve: Curves.easeIn
)

Curves类定义的动画曲线:

1)linear:匀速动画;

2)decelerate:匀减速动画;

3)ease:先加速后减速动画;

4)easeIn:先快后慢动画;

5)easeOut:先慢后快动画;

6)easeInOut:先慢,然后加速,最后减速动画。

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import 'package:flutter/material.dart';

void main() => runApp(CurvePage());

class CurvePage extends StatefulWidget {
  State<StatefulWidget> createState() {
    return CurveState();
  }
}

class CurveState extends State<CurvePage> with SingleTickerProviderStateMixin{
  CurvedAnimation curve;
  AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: const Duration(milliseconds: 5000),
      vsync: this,
    );
    curve = CurvedAnimation(
      parent: controller,
      curve: Curves.easeIn
    )..addListener(() {
      setState(() {});
    });
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动画',
      home: Scaffold(
        appBar: AppBar(title: Text('动画')),
        body: Center(
          child: Container(
            margin: EdgeInsets.symmetric(vertical: 10),
            height: 300 * curve.value,
            width: 300 * curve.value,
            child: FlutterLogo()
          )
        )
      )
    );
  }

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

运行上面的代码,动画会在5秒内执行先慢后快的非线性动画,图标慢慢变大。

8.1.4 Tween

默认情况下,AnimationController创建的动画对象的取值范围是[0.0, 1.0],可以使用Tween来自定义范围。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Tween doubleTween = Tween<double>(begin: 0.0, end: 100.0);

Tween是一个无状态对象,它继承自Animatable<T>,Animatable是一个控制动画类型的类,定义了动画值的映射规则,需要传入begin和end两个参数。

Animatable支持多取值类型,如数字、颜色等。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Tween colorTween = ColorTween(
  begin: Colors.white,
  end: Colors.black
);

如果需要使用Tween对象,可以调用其animate(),然后传入一个控制器对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
AnimationController controller = AnimationController(
  duration: const Duration(milliseconds: 500),
  vsync: this
);

Animation<int> alpha = IntTween(
  begin: 0,
  end: 255
).animate(controller);

8.2 动画组件

8.2.1 基础用法

使用Animation和AnimationController实现心跳动画示例。

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import 'package:flutter/material.dart';

void main() => runApp(HeartAnimPage());

class HeartAnimPage extends StatefulWidget {
  HeartAnimPage({Key key}): super(key: key);
  _HeartAnimState createState() => _HeartAnimState();
}

class _HeartAnimState extends State<HeartAnimPage> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation<double> animation;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this
    );
    animation = CurvedAnimation(
      parent: controller,
      curve: Curves.easeInOut,
      reverseCurve: Curves.easeOut
    );
    animation.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        controller.reverse();
      } else if (status == AnimationStatus.dismissed) {
        controller.forward();
      }
    });
    animation = Tween(begin: 50.0, end: 120.0).animate(controller);
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动画--基础用法',
      home: Scaffold(
        appBar: AppBar(title: Text('动画--基础用法')),
        body: Center(
          child: AnimatedBuilder(
            animation: animation,
            builder: (ctx, child) {
              return Icon(Icons.favorite, color: Colors.red, size: animation.value);
            },
          )
        )
      )
    );
  }

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

示例效果:

8.2.2 AnimatedWidget

Flutter官方提供了AnimatedWidget组件,用于简化动画开发中addListener()和setState()的调用流程。

使用AnimatedWidget组件重构心跳动画的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import 'package:flutter/material.dart';

void main() => runApp(HeartAnimPage());

class HeartAnimPage extends StatefulWidget {
  HeartAnimPage({Key key}): super(key: key);
  _HeartAnimState createState() => _HeartAnimState();
}

class _HeartAnimState extends State<HeartAnimPage> with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation<double> animation;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this
    );
    animation = CurvedAnimation(
      parent: controller,
      curve: Curves.easeInOut,
      reverseCurve: Curves.easeOut
    );
    animation.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        controller.reverse();
      } else if (status == AnimationStatus.dismissed) {
        controller.forward();
      }
    });
    animation = Tween(begin: 50.0, end: 120.0).animate(controller);
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动画--AnimatedWidget',
      home: Scaffold(
        appBar: AppBar(title: Text('动画--AnimatedWidget')),
        body: Center(
          child: HeatAnimatedWidget(animation)
        )
      )
    );
  }

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

class HeatAnimatedWidget extends AnimatedWidget {
  HeatAnimatedWidget(Animation animation): super(listenable: animation);

  @override
  Widget build(BuildContext context) {
    Animation animation = listenable;
    return Icon(Icons.favorite, color: Colors.red, size: animation.value);
  }
}

AnimatedWidget组件有效地将业务逻辑和功能组件分离,简化了动画操作的逻辑。

8.2.3 AnimatedBuilder

使用AnimatedBuilder组件, 系统只会重新构建动画组件自身,对父子组件不做任何处理,从而避免不必要的性能开销。

使用AnimatedBuilder组件还有以下优点:

1)不需要显示添加帧监听器以及调用setState();

2)缩小动画构建的范围,避免不必要的视图构建,从而提高视图渲染性能;

3)可以封装一些常见的动画效果,从而提高代码的复用性。

8.3 转场动画

在原生Android开发中,可以使用共享元素动画(Shared Element Transition,又称Hero Transition)来实现多个页面的切换动画。

Hero指的是可以在路由(即Flutter页面)之间飞行的组件。

在Flutter中,实现Hero动画效果至少需要两个路由,即源路由和目标路由,然后使用Hero组件包裹在需要动画控制的组件外面,同时为它们设置相同的tag属性。

Hero动画组件的构造函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const Hero({
  Key key,
  @required this.tag,//Hero组件的标识,两个Hero组件就是通过tag标识关联起来的
  this.createRectTween,
  this.flightShuttleBuilder,
  this.placeholderBuilder,
  this.transitionOnUserGestures = false,
  @required this.child,
});

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import 'package:flutter/material.dart';

void main() => runApp(HeroPage());

class HeroPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动画--转场动画',
      home: HeroAPage(),
    );
  }
}

class HeroAPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('第一个页面')),
      body: Container(
        child: Center(
          child: GestureDetector(
            child: Hero(
              tag: 'avatar',
              child: Image.asset(
                'images/wx.png',
                 width: 100, 
                 height: 100, 
                 fit: BoxFit.fill
              ),
            ),
            onTap: () {
              Navigator.push(
                context, 
                MaterialPageRoute(builder: (BuildContext context) => HeroBPage())
              );
            }
          )
        )
      )
    );
  }
}

class HeroBPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('第二个页面')),
      body: Container(
        child: Center(
          child: GestureDetector(
            child: Hero(
              tag: 'avatar',
              child: Image.asset(
                'images/wx.png',
                 width: 300, 
                 height: 300, 
                 fit: BoxFit.fill
              ),
            ),
            onTap: () {
              Navigator.pop(context);
            }
          )
        )
      )
    );
  }
}

示例效果:

8.4 交错动画

在Flutter中,渐变、平移、缩放和旋转动画都属于基础动画,如果要实现一些复杂的动画效果,可以把这些基础动画组合起来形成一个动画序列或重叠动画,Flutter将这些动画序列或重叠动画称为交错动画。

在Flutter开发中,使用交错动画需要满足以下几点:

1)创建交错动画时需要创建多个动画对象;

2)一个AnimationController动画控制器控制所有的动画对象;

3)给每一个动画对象指定时间间隔,且动画的时间间隔为[0.0, 1.0]。

使用交错动画实现Flutter图标缩放和渐变的动画示例。

示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import 'package:flutter/material.dart';

void main() => runApp(ParallelWidget());

class ParallelWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动画--交错动画',
      home: ParallelPage(),
    );
  }
}

class ParallelPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return ParallelState();
  }
}

class ParallelState extends State<ParallelPage> with TickerProviderStateMixin {
  AnimationController controller;
  Animation<double> animation;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 3000)
    );
    animation = CurvedAnimation(
      parent: controller,
      curve: Curves.bounceIn
    );
    animation.addStatusListener((status) {
      if(status == AnimationStatus.completed) {
        controller.reverse();
      } else if (status == AnimationStatus.dismissed) {
        controller.forward();
      }
    });
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return ParallelAnimatedWidget(animation: animation);
  }

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

class ParallelAnimatedWidget extends AnimatedWidget {
  final _opacityTween = Tween<double>(begin: 0.1, end: 1.0);
  final _sizeTween = Tween<double>(begin: 0.0, end: 300.0);

  ParallelAnimatedWidget({Key key, Animation<double> animation}): super(key: key, listenable: animation);

  Widget build(BuildContext context) {
    Animation<double> animation = listenable;
    return Center(
      child: Opacity(
        opacity: _opacityTween.evaluate(animation),
        child: Container(
          margin: EdgeInsets.symmetric(vertical: 10.0),
          height: _sizeTween.evaluate(animation),
          width: _sizeTween.evaluate(animation),
          child: FlutterLogo()
        ),
      ),
    );
  }
}

示例效果:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱学习的程序媛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Centos下安装FastDFS
FastDFS运行需要一些依赖,在课前资料提供的虚拟中已经安装好了这些依赖,如果大家想要从头学习,可以按下面方式安装:
用户4396583
2024/07/25
1680
Nginx编译安装参数详解
./configure --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_dav_module --with-http_stub_status_module --with-threads --with-file-aio
非著名运维
2022/06/22
1.6K0
Nginx编译安装参数详解
Nginx总结(一)如何安装Nginx【详细教程】
以前写过一些Nginx的文章,但都是用到什么说什么,没有一个完整系统的总结。趁最近有时间,打算将Nginx相关的内容重新整理一下。nginx系列文章地址如下:https://www.cnblogs.com/zhangweizhong/category/1529997.html
章为忠学架构
2020/01/17
12.9K0
Nginx总结(一)如何安装Nginx【详细教程】
nginx基础5
Tip: 以下是可配置的选项,不加参数会按默认特性配置 [root@h102 nginx-1.9.5]# ./configure --help --help print this message --prefix=PATH set installation prefix --sbin-path=PATH set nginx binary pathname
franket
2022/05/12
1820
Nginx 模块(1)
Nginx (engine x) 可以作为 HTTP 和反向代理服务器,也可以作为邮件代理和普通的 TCP/UDP 代理服务器
franket
2021/10/18
5050
Linux下安装和使用Nginx
Nginx (“engine x”) 是一个高性能的 HTTP和反向代理服务器,也是一个 IMAP/POP3/SMTP 服务器。 正向代理:
共饮一杯无
2022/11/24
1.2K0
Linux下安装和使用Nginx
Nginx(一)安装及启停
    我发现很多博客排版杂乱,表达不清,读者看了往往云里雾里。我此前的博客也是如此,我自己很不满意。今起,每一篇博客都会用心写,此前的博客我也会尽力修改。至少要做到排版整洁,让自己满意,让别人能看懂。
用户1432189
2018/09/05
8860
Nginx(一)安装及启停
Nginx服务实践
注意下载页面最好选择稳定版:http://nginx.org/en/download.html
陈雷雷
2019/07/10
8490
重识Nginx - 02 手把手教你编译适合自己的nginx 1.22.0
文章目录 编译Nginx 下载nginx 1.22.0 step 2 解压 编译 (这里我们只指定 prefix,其他默认 ) make 编译 make install 安装 Configure的命令参数 编译Nginx 下载nginx 1.22.0 https://nginx.org/en/download.html 复制链接地址 ,下载 1.22.0 [root@VM-0-7-centos ng]# wget https://nginx.org/download/nginx-1.22.0.
小小工匠
2022/09/27
8260
重识Nginx - 02 手把手教你编译适合自己的nginx 1.22.0
nginx动态添加模块
有时候我们在环境部署nginx时,由于环境初期较简单,随着后期业务发展,需要的功能越来越多时,可能我们最开始编译安装的nginx已经无法满足我们现在的需求了,比如说,我们想使用nginx的缓存功能,想使用nginx的连接限制模块等,这时我们就需要在不覆盖之前已经编译好的nginx来动态添加所需的模块了。
dogfei
2020/07/31
2.1K0
2.Nginx进阶学习之最佳配置实践指南
描述:在企业线上生产环境中推荐进行Nginx编译安装,可以按照业务侧重点进行相应 Nginx 编译参数配置,所以编译参数不是功能加的越多越好,应该尽可能少编译模块不用的最好不要加入,本小结将以最新的Nginx版本以及依赖版本进行编译演示。
全栈工程师修炼指南
2022/09/29
2K0
2.Nginx进阶学习之最佳配置实践指南
Nginx 安装
Nginx 是 C语言 开发,建议在 Linux 上运行,当然,也可以安装 Windows 版本,本篇则使用 CentOS 7 作为安装环境。
用户5760343
2022/05/23
2920
Nginx 安装
LNMP之Nginx
Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。 其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/PO
用户1173509
2018/03/28
1.1K0
LNMP之Nginx
OpenNJet如何做到让用户永远在线
最近看到了国内开源的一个名为OpenNJet的项目,有一个响亮的口号:“下一代云原生应用引擎”。
手撕代码八百里
2024/05/24
1180
OpenNJet如何做到让用户永远在线
Nginx入门详解文档
Nginx入门详解文档 1 文章内容 掌握nginx+tomcat反向代理的使用方法。 掌握nginx作为负载均衡器的使用方法。 掌握nginx实现web缓存方法。 2 nginx介绍 2.1 什么是nginx Nginx是一款高性能的http 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。官方测试nginx能够支支撑5万并发链接,并且cpu、内存等资源消耗却非常低,运行非常稳定。 3 nginx安装 3.1 下载 进入http://nginx.org/en/download.html
Java帮帮
2018/03/15
1.4K0
Nginx入门详解文档
爬虫、代理和Nginx
做过爬虫的人应该都知道,抓的网站和数据多了,如果爬虫抓取速度过快,免不了触发网站的防爬机制,几乎用的同一招就是封IP。解决方案有2个:
后端技术探索
2018/10/18
2.2K0
爬虫、代理和Nginx
Nginx环境准备与安装
进入到 Nginx 解压包目录/usr/local/Nginx-1.20.1 目录中,查看 Nginx 的目录。
会洗碗的CV工程师
2024/06/09
1680
Nginx环境准备与安装
Nginx实用模块
Nginx 是一个很强大的高性能Web和反向代理应用。原先一直停留在apt install nginx 的阶段,直到开始深入了解其模块等,才发现:nginx厉害!!nginx除了基础功能以外还有很多有趣且实用的模块。因为nginx内置了许多变量(http://nginx.org/en/docs/varindex.html 部分是模块实现)、逻辑运算、指令,组合起来,就可以实现强大的功能。
yumusb
2020/04/22
9690
Nginx教程
Nginx是一款高性能的http 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。由俄罗斯的程序设计师Igor Sysoev所开发,官方测试nginx能够支支撑5万并发链接,并且cpu、内存等资源消耗却非常低,运行非常稳定。
红目香薰
2022/11/29
5570
Nginx教程
Nginx编译安装nginx-upsync-module模块以实现动态负载
【转载请注明出处】:https://cloud.tencent.com/developer/article/1627571
后端老鸟
2020/05/13
1.2K0
Nginx编译安装nginx-upsync-module模块以实现动态负载
相关推荐
Centos下安装FastDFS
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档