前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ConstraintLayout2.0一篇写不完之Stagger交错

ConstraintLayout2.0一篇写不完之Stagger交错

作者头像
用户1907613
发布2021-09-29 15:34:38
5150
发布2021-09-29 15:34:38
举报
文章被收录于专栏:Android群英传
在Flutter中,有个StaggerAnimation,可以实现交错动画效果,这个效果相当于在申明式编程中为多个动画增加了多条时间线,在Android中,以往要实现交错动画效果,需要为每个属性动画设置Delay时间,或者监听其生命周期,而在MotionLayout中,可以直接在xml中设置交错动画的驱动流程,极大的简化了动画的创建。

在MotionLayout中,它为每个被标记了motionStagger的View分配了一个float value(没有标记的View不会被引入交错动画),float value最小的(V0)的View首先被启动。float value最高的(Vn)的View最后启动。

这是motionStagger的动画总纲,但是具体的启动时间和执行顺序,由下面的这些参数来精确控制。

  • MotionLayout中每个VIew的motionStagger value被标记为S(Vi)
  • 总的Stagger Value被标记为stagger,取值为0.0-1.0,通常在Transition中指定
  • 在Transition中指定动画的duration

在这基础上,motionStagger定义了下面两条规则:

  • View的动画持续时长 = duration * (1 - stagger)
  • View的动画启动时间点算法为:duration * (stagger - stagger * (S(Vi) - S(V0)) / (S(Vn) - S(V0)))

其中最奇怪的就是这个参数:(S(Vi) - S(V0)) / (S(Vn) - S(V0))

我们可以梳理下,我们给MotionLayout中的所有需要做StaggerAnimation的View标记了motionStagger value,这些元素形成了一个数组,从大到小进行排序:

【Sv0—Sv1—Sv2—Sv3—Sv4—Sv5】

在这个数组只,(S(Vi) - S(V0)) / (S(Vn) - S(V0))这个表达式所具有的几何含义是什么呢?实际上就是点阵的Manhattan distance(曼哈顿距离),具体如下所示。

image-20210830144923418

好了,看到这里还不走的朋友,心里也忍不住会想了,这TM是什么鬼?我写个动画还TM要这些??

是的,有的复杂,我们先举个例子来看下。

首先,我假定设置MotionLayout中有3个View——View1、View2、View3,分别设置motionStagger value为7,5,3,再给Transition设置staggered为0.6,duration为5000,这些都是我假设的,我们来看下这个状态下,MotionLayout的StaggerAnimation是如何创建的。

首先,计算duration。

代码语言:javascript
复制
View的动画持续时长 = duration * (1 - stagger)
即:View的动画持续时长 = 5000 * (1 - 0.6) = 2000

接下来,针对每个View计算,所以,Sv1= 7,Sv2= 5,Sv3= 3。

代码语言:javascript
复制
duration * (stagger - stagger * (S(Vi) - S(V0)) / (S(Vn) - S(V0)))
即:
View1:启动于 5000 * (0.6 - 0.6 * (7 - 3)/(7 - 3)) = 0
       终止于 0 + 2000 = 2000

View2:启动于 5000 * (0.6 - 0.6 * (5 - 3)/(7 - 3)) = 1500
       终止于 1500 + 2000 = 3500
       
View3:启动于 5000 * (0.6 - 0.6 * (3 - 3)/(7 - 3)) = 3000
       终止于 3000 + 2000 = 5000

是不是有点意思。

我感觉没意思,难道我写动画还要这样凑数字吗?

如果我们要从正向来理解这些公式的含义,那么要将上面的公式进行一下变形处理。

代码语言:javascript
复制
startPoint = duration * (stagger - stagger * (S(Vi) - S(V0)) / (S(Vn) - S(V0)))

再理清楚一点:

代码语言:javascript
复制
animationStartTime = totalDuration * (stagger - stagger * ((staggerCurrentView - lowestStaggerValue)/(highestStaggerValue - lowestStaggerValue))

totalDuration好说,剩下的就是stagger值的确定了。

我们同样用之前那个例子,我假定设置MotionLayout中有3个View——View1、View2、View3,三个View依次出现。

前面我们的公式指出了:viewDuration = totalDuration * (1 - stagger),那么将其变形处理后,我们就得到了stagger:

代码语言:javascript
复制
stagger = 1 - (viewDuration / totalDuration)

所以,如果我要三个动画依次出现,那么viewDuration / totalDuration就是1/3,也就是说stagger约为0.6。

确定了stagger之后,我们再来看duration,由于stagger确定,所以totalDuration可以自由设置,viewDuration会根据其它两个参数动态变化。

那么每个View的motionStagger呢?实际上在开发动画的时候,通常都是先使用递减数列或者递增数列来做(取决于你的视图展示顺序),再根据动画参数进行微调,例如前面的例子,我们可以给View1、2、3分别设置motionStagger为3、2、1,那么启动顺序如下所示。

代码语言:javascript
复制
totalDuration = 5000,stagger = 0.6,viewDuration = 2000
即:
View1:启动于 5000 * (0.6 - 0.6 * (3 - 1)/(3 - 1)) = 0
       终止于 0 + 2000 = 2000

View2:启动于 5000 * (0.6 - 0.6 * (2 - 1)/(3 - 1)) = 1500
       终止于 1500 + 2000 = 3500
       
View3:启动于 5000 * (0.6 - 0.6 * (1 - 1)/(3 - 1)) = 3000
       终止于 3000 + 2000 = 5000

有没有发现,和前面的结果是一致的!

越来越难了不是吗,写个动画还得用这么多数学公式!

其实不用。

最后我们来总结下,一句话让你了解Stagger。

❝当MotionLayout中的所有View的motionStagger value递增或者递减时,在Transition中设置的staggered控制的就是每个View启动的时间间隔,staggered value越小,间隔越短,极端下,为0时,没有Stagger效果,为1时,每个View动画完成后才执行下一个。 ❞

以上。

当然,你不懂这些公式也能写,但是懂了能让你写的更清晰,这就是「凑」和「算」的区别。

向大家推荐下我的网站 https://xuyisheng.top/ 点击原文一键直达

专注 Android-Kotlin-Flutter 欢迎大家访问

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

本文分享自 群英传 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档