ConstraintLayout2.0一篇写不完之KeyCycles的秘密

2021-09-29 15:33:35 浏览数 (1)

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

KeyCycle与KeyFrame类似,但是又比KeyFrame复杂,复杂在于KeyFrame只是单帧,而KeyCycle则是在KeyFrame的基础上,增加了周期性的处理,所以,KeyCycle的核心就是周期,KeyCycle决定了在Scene中所有需要重复处理的部分操作,它的核心API如下所示。

  • framePosition:作为一个KeyFrame,KeyCycle必须知道在场景的哪一点上进行操作
  • motionTarget:指定的View ID
  • wavePeriod:周期数量
  • waveOffset:起始位置的偏移
  • waveShape:Cycle的波形

image-20210827101705951

MotionLayout提供了CycleEditor来帮助开发者编辑KeyCycle,下载地址如下:

https://github.com/googlesamples/android-ConstraintLayoutExamples/releases/download/1.0/CycleEditor.jar

直接执行即可,点击file中的parse,就可以将编辑区域的xml转换为波形。

代码语言:javascript复制
java -jar CycleEditor.jar

分割Scene

在创建KeyCycle之前的第一件事,就是通过使用不同的framePositions把你的Scene分成多部分组合的Partial Scene,接下来就可以通过使用wavePeriod来指定你想要的每个部分的周期数,以及waveShape来指定具体的波形。

1*cdtzWO2JKu6VvmN9Ew2kvw

wavePeriod是KeyCycle最难理解的一部分,要掌握wavePeriod的定义,就必须先了解Partial Scene,Partial Scene指的是当前指定点的前一个点和后一个点,总共三个点构成的区域,这点非常重要。

在某个framePosition的KeyCycle中指定wavePeriod,其实就是指在这个Partial Scene中,有几个周期的波形来填满这个区域。

但是这里问题又来了,每个framePosition都被周围的framePosition有关,那么wavePeriod不是被重复计算了吗?

没错。。。所以在整个Partial Scene中的wavePeriod是由Partial Scene中所有framePosition的的wavePeriod之和确定的。很绕是不是,是就对了。

这也是为什么KeyCycle有个单独的生成工具的原因,结合KeyCycleEditor,还是比较能理解的。

wavePeriod已经很绕了,但是绕的还在后面。

我们再来看看KeyCycle中指定的具体属性值的含义。

例如,我们在KeyCycle中指定rotation为20,代码如下所示。

代码语言:javascript复制
<KeyCycle 
        motion:framePosition="0"
        motion:target="@ id/button"
        motion:wavePeriod="0"
        motion:waveOffset="0"
        motion:waveShape="sin"
        android:rotation="20"/>

这个rotation为20是什么意思?你以为是当然framePosition的属性值为20吗?太年轻了。。。

其实这个属性值与View在当前framePosition的属性值,并没有直接联系。。。

是不是很奇怪,的确如此,那么这玩意儿到底是干嘛的呢???

这里我们需要转换一下思路,那就是KeyCycle里面设置的一切东西,都是为了画出「波形图」,所以,这些参数的设置,就是为了修改波形图的具体形状。

代码语言:javascript复制
<KeyFrameSet>

<KeyCycle 
        motion:framePosition="0"
        motion:target="@ id/button"
        motion:wavePeriod="0"
        motion:waveOffset="0"
        motion:waveShape="sin"
        android:rotation="0"/>

<KeyCycle 
        motion:framePosition="50"
        motion:target="@ id/button"
        motion:wavePeriod="1"
        motion:waveOffset="0"
        motion:waveShape="sin"
        android:rotation="10"/>

<KeyCycle 
        motion:framePosition="100"
        motion:target="@ id/button"
        motion:wavePeriod="0"
        motion:waveOffset="0"
        motion:waveShape="sin"
        android:rotation="30"/>

</KeyFrameSet>

这样一个KeyCycle最后形成的波形图就是这样。

image-20210827151332911

由此可以发现,每个framePosition的属性值,就是为了画出波形图的波峰。

在这个的基础上,waveOffset就好理解了,它的作用就是给framePosition的当前value增加一个初始值,这个初始值同样是为了修改波形。

要干嘛

你说KeyCycle这玩意儿整这么复杂,到底有什么用呢??

我们有了KeyFrame,可以用来添加中间态关键帧,那么还要KeyCycle干嘛呢?

说到这来,就不能不提下Monotonic Spline(单调采样)了,通常的关键帧插值算法都是使用的单调采样,但是这样无法做到曲线的圆滑过渡,就像下图中的绿色曲线,这样四个点使用单调采样,就变成了下面这样的曲线,过渡会非常生硬。

image-20210827154425111

那么为了让曲线圆滑过渡,KeyCycle使用的是Typical Spline(特征采样),就如上图中的紫色曲线,四个点被圆滑的连接了起来。

如果仅仅是为了让曲线能圆滑过渡,那么你就太小看KeyCycle了,不得不说老外做的这些东西,总能在一些你觉得无关紧要的地方,做的非常深入。

KeyCycle的核心在于波形,而波是什么呢?

image-20210827155302534

上面这张图表达了sin和cos的几何含义,也是sin和cos的来源。

说句不像傅里叶变换的话,我们可以将一个View的曲线运动,拆解成多个不同波形运动的叠加。

例如我们对一个View的translationX同时设置sin和cos的KeyCycle,最终形成的运动轨迹,就是一个圆形!

所以,由此及彼,我们可以复合多个属性的同时,通过不同的波形叠加,实现任何你想要的运动轨迹!这TM就牛逼了啊,简直就是傅里叶变换在Android动画中的实现了。

在CycleEditor中,有一些自带的Demo,可以让你充分的了解这个思想,例如下面这个例子。

image-20210827160218065

太复杂了是吗?

CustomWave shape in keyCycle

CL2.1之后,motion:waveShape除了之前定义的sin、cos、bounce这些预设曲线外,你还可以设置自定义的波形曲线,定义方式如下所示。

代码语言:javascript复制
<KeyCycle motion:waveShape=”spline(0.0, 1.0, -1.0, 0)” />

这就有点牛逼了,本来就很复杂了,这下还来了自定义曲线,再见。

KeyCycle确实比较强大,但是也非常复杂,强烈建议大家使用CycleEditor来学习,KeyCycle这种东西,就像核武器一样,可以不用,但是不能没有。

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

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

往期推荐

  • flutter与compose的爱恨情仇
  • 从精准化测试看ASM在Android中的强势插入-读懂diff
  • 闲言碎语——第四期
  • 真·富文本编辑器的演进之路-Span的整体性控制

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

< END >

作者:徐宜生

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

0 人点赞