Flutter利用Hero组件实现自定义路径效果的动画
作者:岛上码农 发布时间:2023-06-25 13:46:29
前言
我们在 页面切换转场动画,英雄救场更有趣!介绍了 Hero 动画效果,使用 Hero 用于转场能够提供非常不错的体验。既然称之为英雄,肯定还有其他技能,本篇我们就来探索一下 Hero 动画的返回效果。
Hero 的定义
Hero 组件是一个 StatefulWidget,构造方法如下:
const Hero({
Key? key,
required this.tag,
this.createRectTween,
this.flightShuttleBuilder,
this.placeholderBuilder,
this.transitionOnUserGestures = false,
required this.child,
})
其中 createRectTween
就是一个矩形插值,用于控制 Hero 组件的路径。实际上,和普通动画一样,也是有一个时间曲线,取值范围是0-1.0,然后createRectTween
保证 Hero 组件动画前后能够达到矩形指定位置和大小。下面一张图是官网的说明图:
image.png
RectTween
RectTween 和 Tween类似,实际上就是矩阵在动画过程中的变化。我们来看 RectTween 的定义:
class RectTween extends Tween<Rect?> {
RectTween({ Rect? begin, Rect? end }) : super(begin: begin, end: end);
/// 通过给定的动画时间值构建新的插值矩形
@override
Rect? lerp(double t) => Rect.lerp(begin, end, t);
}
这个类很简单,其实就是每次动画时间点上调用 Rect.lerp
构建一个插值的矩形。Rect.lerp 方法如下:
static Rect? lerp(Rect? a, Rect? b, double t) {
assert(t != null);
if (b == null) {
if (a == null) {
return null;
} else {
final double k = 1.0 - t;
return Rect.fromLTRB(a.left * k, a.top * k, a.right * k, a.bottom * k);
}
} else {
if (a == null) {
return Rect.fromLTRB(b.left * t, b.top * t, b.right * t, b.bottom * t);
} else {
return Rect.fromLTRB(
_lerpDouble(a.left, b.left, t),
_lerpDouble(a.top, b.top, t),
_lerpDouble(a.right, b.right, t),
_lerpDouble(a.bottom, b.bottom, t),
);
}
}
}
在矩形 a 和矩形 b 都不为空的时候,返回的就是一个通过定点定义的新的矩形。这里的关键是_lerpDouble 方法,其实最终就是根据动画时间完成顶点的移动。
double? lerpDouble(num? a, num? b, double t) {
/// ...
return a * (1.0 - t) + b * t;
}
也就是从矩形 a 的顶点逐步移动到矩形 b的顶点,从而完成了两个矩形的动画过渡。有了这个基础我们就可以构建自定义的 RectTween 了。和我们的之前说过的动画曲线(动画曲线天天用,你能自己整一个吗?看完这篇你就会了!)是类似的。
自定义RectTween
我们来一个自定义 RectTween
,然后保证起始点是矩形 a,结束点是矩形 b,然后中间沿曲线变动就可以了。下面是我们利用曲线将时间转换后得到的一个自定义 RectTween
。其中使用曲线转换后的transformT
取值还是从0-1.0,然后使用_rectMove
方法就能实现从开始的矩形过渡到结束的矩形了。
class CustomRectTween extends RectTween {
final Rect begin;
final Rect end;
CustomRectTween({required this.begin, required this.end})
: super(begin: begin, end: end);
@override
Rect lerp(double t) {
double transformT = Curves.easeInOutBack.transform(t);
var rect = Rect.fromLTRB(
_rectMove(begin.left, end.left, transformT),
_rectMove(begin.top, end.top, transformT),
_rectMove(end.right, end.right, transformT),
_rectMove(begin.bottom, end.bottom, transformT));
return rect;
}
double _rectMove(double begin, double end, double t) {
return begin * (1 - t) + end * t;
}
}
运行效果
可以看到结束的时候,有个回弹效果,那是因为使用了Curves.easeInOutBack
这个曲线。源码已上传至:动画相关源码。
运行效果
来源:https://mp.weixin.qq.com/s/irXxttIRDFyGkPJyUTnp8w


猜你喜欢
- 1.场景介绍:开发过程中我们经常性的会用到许多的中间表,用于数据之间的对应和关联.这个时候我们关联最多的就是ID,我们在一张表中插入数据后级
- 其实是可以通过@Constraint来限定自定义注解的方法。@Constraint(validatedBy = xxxx.class)下面是
- 简要介绍Retrofit是当前应用非常广泛的网络请求框架,通常结合RxJava来进行网络请求,本文将展示一个采用RxJava+Retrofi
- [DllImport("User32.dll", CharSet = CharSet.Auto)]  
- IntelliJ IDEA简称IDEA,是常用的java开发工具,相对eclipse在使用上入门较难,但在编写java代码方面比较eclip
- merge标签合并标记需要两个或两个以上的列表作为参数,并把它们合并在一起,如下所示:<s:merge var="myMer
- 前言SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文
- Android 代码写控件代替XML简单实例简单的一个Button控件的练习。实现代码:Button btn = new Button(Ha
- 本文实例讲述了Android开发中使用外部应用获取SD卡状态的方法。分享给大家供大家参考,具体如下:先来看看常规获取SD卡状态的方法if (
- 1.连接池在实际开发中都会使用连接池因为它可以减少我们获取连接所消耗的时间连接池就是用于存储连接的一个容器,容器其实就是一个集合对象,该集合
- MyBatis Plus插件MyBatis Plus提供了分页插件PaginationInterceptor、执行分析插件SqlExplai
- 本文实例讲述了C#利用System.Uri转URL为绝对地址的方法。分享给大家供大家参考。具体分析如下:在使用ASPOSE.Word生成Wo
- 今天使用jenkins构建时,报以下错误[ERROR] Failed to execute goal on project saas20:
- 现象说明maven的java项目,测试用例和main所在的源码文件均符合缺省写法和格式,但是在使用mvn clean sonar:sonar
- 依赖配置结合前面的内容,这里我们要嵌入数据库的操作,这里以操作MySQL为例整合Mybatis,首先需要在原来的基础上添加以下依赖<!
- 代码案例一:private void button1_Click(object sender, EventArgs e) &n
- 1、java.util.concurrent.atomic 的包里有AtomicBoolean, AtomicInteger,AtomicL
- 一、什么是桥接模式桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是一种对象结构型模式,又
- 很多情况下sql不好解决的多表查询,临时表分组,排序,尽量用java8新特性stream进行处理使用java8新特性,下面先来点基础的Lis
- 本文实例讲述了Java单例模式。分享给大家供大家参考,具体如下:在实际开发的时候会有一些需求,在某个类中只能允许同时存在一个对象。这时就需要