思路参考自: 扔物线
整体效果
话不多少,直接上效果
通过观察可以发现这个动画分为三个过程
-
过程一: 底部翘起来
-
过程二: 转起来
过程三:右边翘起来
三维图像投影到二维平面
图片绕着 x 轴旋转,左侧视图为旋转后投影到二位平面的图片,右侧为旋转过程中的三维视图。
过程一
可以把图片分成上下两部分,上半边完全没动,下半部分绕着 x 轴旋转,不断改变转动角度就可以达到过程一的效果
过程二
过程二稍复杂,先看其中某一帧的情况
红线下半部分翘起来了,上半部分没有翘起来,所以考虑分为上下两部分绘制
下半部分
- 图片绕着 z 轴旋转 20 度
- 裁剪图片,只取下半部分
- 图片绕着 x 轴旋转 45 度
- 图片绕着 z 轴旋转 -20 度
上半部分
- 图片绕着 z 轴旋转 20 度
- 裁剪图片,只取上半部分
- 图片绕着 x 轴旋转 0 度(为什么?为了和其他过程统一过程,方便代码编写)
- 图片绕着 z 轴旋转 -20 度
拼接
把这两部分图拼接起来就是过程二中某一帧的效果
实现过程二的动画
保持每一帧 绕着 x 轴旋转的角度固定,改变绕着 z 轴旋转的角度就可以实现过程二的动画。
改进过程一(方便代码编写)
过程一下半部分
- 图片绕着 z 轴旋转 0 度
- 裁剪图片,只取下半部分
- 图片绕着 x 轴旋转某个角度
- 图片绕着 z 轴旋转 0 度
不断改变 x 轴旋转的角度就可以就可以实现过程一中下半部分的动画效果
过程一上半部分
- 图片绕着 z 轴旋转 0 度
- 裁剪图片,只取上半部分
- 图片绕着 x 轴旋转 0 度
- 图片绕着 z 轴旋转 0 度
过程三
过程三和过程一类似,不再赘述。
整个动画具体参数
-
过程一:
- 上半部分:旋转角度都是 0
- 下半部分:绕 z 轴旋转角度始终为 0,绕 x 轴旋转角度从 0 过渡到 -45 度
-
过程二:
- 上半部分:绕着 z 轴旋转角度从 0 过渡到270 度,绕着 x 轴旋转的角度固定为 0 度
- 下半部分:绕着 z 轴旋转角度从 0 过渡到270 度,绕着 x 轴旋转的角度固定为 -45 度
-
过程三
- 上半部分:绕 z 轴旋转角度始终为 270 度,绕 x 轴旋转角度从 0 过渡到 45 度
- 下半部分:绕 z 轴旋转角度始终为 270 度,绕 x 轴旋转角度始终为 0 度
代码编写
首先定义一个enum,标识动画当前进行到那个过程
enum FlipAnimationSteps {
animation_step_1, animation_step_2, animation_step_3 }
设置动画参数,监听动画状态
class _FlipAnimationApp extends State<FlipAnimationApp>
with SingleTickerProviderStateMixin {
var imageWidget = Image.asset(
'images/mario.jpg',
width: 300.0,
height: 300.0,
);
AnimationController controller;
CurvedAnimation animation;
@override
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 1), vsync: this);
animation