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

Android群英传

共 4306字,需浏览 9分钟

 ·

2021-09-08 10:05

点击上方蓝字关注我,知识会给你力量


在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。

View的动画持续时长 = duration * (1 - stagger)
即:View的动画持续时长 = 5000 * (1 - 0.6) = 2000

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

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

是不是有点意思。

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

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

startPoint = duration * (stagger - stagger * (S(Vi) - S(V0)) / (S(Vn) - S(V0)))

再理清楚一点:

animationStartTime = totalDuration * (stagger - stagger * ((staggerCurrentView - lowestStaggerValue)/(highestStaggerValue - lowestStaggerValue))

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

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

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

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,那么启动顺序如下所示。

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 欢迎大家访问



往期推荐


本文原创公众号:群英传,授权转载请联系微信(Tomcat_xu),授权后,请在原创发表24小时后转载。
< END >
作者:徐宜生

更文不易,点个“三连”支持一下👇


浏览 24
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报