糟糕的 2020 年即将过去,今年发生了很多意想不到的事,无论如何,生活还要继续,努力朝着明天出发;今天和尚就简单回顾一下 2020 年自己关于技术的这点事儿,然后继续按计划学习;
突如起来的疫情打乱的工作的节奏,有大半年的时间都是在家里远程办公,无论是生活还是工作多多少少都会有所影响;虽然在家里办公与同事小伙伴交流开会有所不便,但也有部分优点,免去了在北京来回上下班的两个小时,可以利用这段时间更好的休息和学习;
和尚自 2018 年开启了个人公众号后,坚持每周更新一篇原创文章,虽然都是一些小知识点,但是坚持了两年多,还是颇有收获,除了个人技术有所沉淀之外,还认识了很多一起搞技术的朋友,有了更多的交流;之后在【云+社区】增加了文章的曝光同时,还认识了很多坚持写作的大佬,甚至有一些是自己平常学习博客的大佬,获益良多;再之后陆续参加了【云+社区】的各类活动,包括个人阅读清单等,增加了学习的动力,希望努力和坚持会有所回报;
和尚继续上一节中自定义的 ACEPageMenu 滑动菜单,详细介绍一下涉及到的 AnimatedBuilder 动画,在此之前需要先了解 AnimatedWidget;
AnimatedWidget 是一个有状态的 StatefulWidget 小部件,通过指定 Listenable 更改值时重建小部件;AnimatedWidget 对于无状态的窗口小部件比较实用;含有众多子类动画,和尚会在之后的博客中慢慢学习;
abstract class AnimatedWidget extends StatefulWidget {
const AnimatedWidget({
Key key,
@required this.listenable,
}) : super(key: key);
final Listenable listenable;
@protected
Widget build(BuildContext context);
@override
_AnimatedState createState() => _AnimatedState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Listenable>('animation', listenable));
}
}
分析源码可得,AnimatedWidget 主要通过 Listenable 来监听小部件动画,通常是 Animation 或 ChangeNotifier;通过重写 build() 方法来设置动画过程;并在 _AnimatedState 中设置状态的更新 setState();
由此可见,AnimatedWidget 已封装好 setState() 状态更新模块,允许将调用中的动画代码中分离出 Widget,而无需单独维护一个 State 状态来保存动画;
和尚尝试 AnimatedWidget 方式展示一个类似 ACEPageMenu 从顶部滑出的一个小动画效果;
class TopMenuWidget extends AnimatedWidget {
const TopMenuWidget({Key key, AnimationController controller}) : super(key: key, listenable: controller);
Animation<double> get _progress => listenable;
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(50, 100 * _progress.value),
child: Opacity(
opacity: _progress.value,
child: Container(
width: 300.0, height: 100.0,
color: Colors.yellow,
child: Center(
child: GestureDetector(
onTap: () => print('I am from AnimatedWidget !'),
child: Icon(Icons.ac_unit, color: Colors.white))))));
}
}
AnimatedBuilder 也是用于构建动画的通用 Widget,是渲染树中的一个独立的类,适用于要提取单独动画效果的较复杂的 Widget;可自动监听来自 Animation 对象的通知,无需手动调用 addListener();
class AnimatedBuilder extends AnimatedWidget {
const AnimatedBuilder({
Key key,
@required Listenable animation,
@required this.builder,
this.child,
}) : super(key: key, listenable: animation);
final TransitionBuilder builder;
final Widget child;
@override
Widget build(BuildContext context) {
return builder(context, child);
}
}
typedef TransitionBuilder = Widget Function(BuildContext context, Widget child);
分析源码可得,AnimatedBuilder 继承自 AnimatedWidget,只需构造窗口小部件并将其传递给构建器函数即可;其中 TransitionBuilder 在每次动画更改值时调用;其中 child 比较特殊,可以作为优化的方向;
如果 builder 函数包含一个不依赖于动画的子树,则一次构建该子树比在每个动画变更时都重新构建子树更为高效;即在 child 中预先定义好 Widget,AnimatedBuilder 会将其传递到构造器函数中;
和尚尝试 AnimatedBuilder 方式展示一个类似 ACEPageMenu 从底部滑出的一个小动画效果;
return AnimatedBuilder(
animation: _controller,
child: Container(
color: Colors.brown,
height: 100.0, width: 300.0,
child: Center(
child: GestureDetector(
onTap: () => print('I am form AnimatedBuilder !'),
child: Icon(Icons.ac_unit, color: Colors.white)))),
builder: (BuildContext context, Widget child) {
return Transform.translate(
offset: Offset(50, ScreenUtils.getScreenHeight() - _controller.value * 200),
child: Opacity(opacity: _controller.value, child: child));
});
条条大路通罗马,同一效果可以有多种不同的实现方式;AnimatedWidget 和 AnimatedBuilder 使用都很便利,而和尚认为 AnimatedBuilder 在处理复杂动画时更加灵活方便;
和尚在尝试缩放动画过程中,遇到之前不曾注意的地方,即动画起始位置由 origin 和 alignment 共同决定,以 aligment 对齐位置为坐标原点,origin 在此基础上平移起始位置;与 scale 无关;
Transform.scale({
Key key,
@required double scale,
this.origin,
this.alignment = Alignment.center,
this.transformHitTests = true,
Widget child,
})
和尚尝试了几个基本场景:
a. alignment 对齐方式默认居中,origin 起始位置默认为 Offset.zero 时,scale 增大为 1.5 倍;可以看到其初始动画位置仍为 Widget 缩放前的中心位;
return Transform.scale(
scale: _progress.value * 1.5,
origin: Offset.zero,
alignment: Alignment.center,
child: Container(width: 200.0, height: 200.0, color: Colors.yellow.withOpacity(0.8)));
b. alignment 对齐方式默认居中,调整 origin 起始位置为 Offset(50, 50);可以看到其初始动画位置是以 alignment 居中点为原点,水平竖直方向为坐标系轴,平移 origin 位置;
return Transform.scale(
scale: _progress.value,
origin: Offset(50, 50),
alignment: Alignment.center,
child: Container(width: 200.0, height: 200.0, color: Colors.yellow.withOpacity(0.8)));
c. alignment 默认居中右对齐,origin 起始位置为 Offset(50, 50);可以看到其初始动画位置是以 alignment 居中右对齐中点为原点,水平竖直方向为坐标系轴,平移 origin 位置;
return Transform.scale(
scale: _progress.value,
origin: Offset(50, 50),
alignment: Alignment.centerRight,
child: Container(width: 200.0, height: 200.0, color: Colors.yellow.withOpacity(0.8)));
````代码`
AnimatedWidget 和 AnimatedBuilder 案例源码
和尚对动画的源码还不够深入,如有错误,请多多指导!
来源: 阿策小和尚
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。