Flutter 动画集锦之小企鹅跑啊跑
起因
这次想做的动画是和对象聊天的时候.对方发的一个表情
相信这个表情很多人应该在wx中也发过, 小企鹅跑来跑去的非常有意思.今天我们就通过 CustomSingleChildLayout 来实现这个动画.
分析
首先我们先分析一下这张图的一个构成.简单来说,就是一个 不规则的图形在来回上下的左右反复运动. 更深入来看, 小企鹅在 移动过程中手的方向也会随移动方向发生改变 . 但是仔细看, 你会发现, 这实际上是x轴反转的一个镜像.
知道这些基本点后, 我们就可以开始设计我们的动画了~
Coding
准备
正篇
绘制背景
首先, 我们创建一个居中的100*200的黄色背景
Scaffold(
body: Center(
child: Container(
color: Colors.yellow,
height: 100,
width: 200,
),
),
);
绘制小企鹅
在Container 内部创建一个CustomSingleChildLayout , 由于外部已经给了限制, 我们这里不需要额外通过_DemoSingleDelegate 的 getSize(BoxConstraints constraints) 去给外部约束尺寸. 同时, 我们创建一个Image 来加载需要的图片. 如果你有自己喜欢的图片, 也可以自行加载, 值得注意的是, 你也需要和本文一样使用一张正方形图片,否则需要根据图片大小自行适配下. 为了方便后面的计算. 我们在 _DemoSingleDelegate 中, 重写getConstraintsForChild(BoxConstraints constraints) 的方法, 返回值为BoxConstraints.tight(Size(100, 100)) . 这样便可以将小企鹅约束在100*100 的范围内.
CustomSingleChildLayout(
delegate: _DemoSingleDelegate(),
child: Image.asset(
'assets/images/qiuqiu.png',
height: 100,
width: 100,
),
),
效果
小企鹅动起来
在小企鹅绘制到方块中后, 我们怎么让它动起来呢? 这里需 借助AnimationController, 它的作用主要是推动小企鹅运动. 我们知道 AnimationController 有个repeat属性, 天然解决了小企鹅在运动中需要来回奔跑的问题.
AnimationController controller =
AnimationController(vsync: this, duration: const Duration(seconds: 1));
/// 重复运动
controller.repeat(reverse: true);
那么, 如何控制小企鹅的位置呢? 我们可以通过 AnimationController 使用动画器Animation, Animattion可以自定义动画的取值和范围. 这里我们先创建一个Animation
/// 用来记录小企鹅的位移
class _DemoInfo {
double x;
double y;
_DemoInfo(this.x, this.y);
}
class _DemoAnimation extends Animatable<_DemoInfo> {
@override
_DemoInfo transform(double t) {
...
}
}
创建Animation 之后, 我们发现它内部提供了一个 transform(double t) 的方法. 这里的 t 即 AnimationController的value, 也就是当前动画执行的进度. 我们根据进度来反推小企鹅的位置. 实际上小企鹅的位置很像函数中的正弦函数
我们如果按正常的正弦函数的话 实际上我们只有上下移动一次. 正弦的公式是 y = A sin(Bx + C) + D. 周期的计算公式是 2π/B . 也就是说我们 B 越大, 同时间内,周期越短,上下移动的频次就越高. 我们以 B 等于 6 为例:
其中,蓝色为 B 等于 1 的时候,红色为 B 等于 6 的时候. 我们同时间内可以上下移动 6 次, 了解到了这一点后. 我们就可以根据 AnimationController的value 来计算小企鹅相应的位置了.
// 由于小企鹅左右还有空白的部分,我们实际计算中还要减去这部分.
// 因此x轴要从-25开始计算, y轴我们可以自定义初始的高度, 然后
// 改变振幅,也就是公式中的A. 这样我们上下偏移的距离分别就是10.
return _DemoInfo(-25 + 150.0 * t, 30 + 10 * sin(t * 6 * pi));
计算好小企鹅的位置后, 我们就要同步自定义布局的_DemoSingleDelegate . 我们把 Animation 传入_DemoSingleDelegate .
Animation<_DemoInfo>? animation;
_DemoSingleDelegate({required this.animation}) : super(relayout: animation);
这样当动画执行时, 我们就可以同步更新画布了. 让我们来看看小企鹅跑起来什么样吧!
小企鹅转向
我们发现在运动过程中,小企鹅并不会在走到头的时候转向. 反转的本质是对小企鹅的x轴反转, 我们来实现一下.
AnimatedBuilder(
builder: (BuildContext context, Widget? child) {
return Transform.scale(
/// 这里当动画forward运行时,小企鹅往左跑. 在reverse时, 小企鹅右跑
scaleX:animation.status == AnimationStatus.forward ? 1 : -1,
child: child,
);
},
animation: animation,
child: Image.asset(
'assets/images/qiuqiu.png',
height: 100,
width: 100,
),
),
效果
完善界面
小企鹅转向后, 我们发现小企鹅的脚是超出色块的. 这个需要我们手动裁切一下, 我们在 CustomSingleChildLayout 外加上一个ClipRect 就可以啦!
源码
结语
这里是WeninerIo,热爱生活且热爱旅行.如果你对这次的分享感兴趣又或者有什么疑惑, 不妨评论区留言 + 关注.期待下一次更好的相遇.
转载自:https://juejin.cn/post/7172466841317539854