
点击上面的蓝字关注我,知识会给你力量
KeyCycle与KeyFrame类似,但又比KeyFrame复杂,复杂在于KeyFrame只是单帧,而且KeyCycle则是在KeyFrame在此基础上,增加了周期性处理,因此,KeyCycle核心是周期,KeyCycle决定了在Scene所有需要重复处理的操作,其核心API如下所示。
framePosition:作为一个KeyFrame,KeyCycle必须知道在场景的哪一点操作
motionTarget:指定的View ID
wavePeriod:周期数量
waveOffset:偏移开始位置
waveShape:Cycle的波形
MotionLayout提供了CycleEditor帮助开发者编辑KeyCycle,下载地址如下:
https://github.com/googlesamples/android-ConstraintLayoutExamples/releases/download/1.0/CycleEditor.jar
直接执行,点击file中的parse,可以编辑区域xml转换为波形。
java-jarCycleEditor.jar
分割Scene
在创建KeyCycle第一件事是使用不同的东西framePositions把你的Scene分为多部分组合Partial Scene,然后就可以用了wavePeriod指定你想要的每个部分的周期数waveShape指定具体的波形。
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。
<KeyCycle motion:framePosition="0" motion:target="@ id/button" motion:wavePeriod="0" motion:waveOffset="0" motion:waveShape="sin" android:rotation="20"/>
这个rotation20是什么意思?你认为这是理所当然的framePosition属性值是20吗?太年轻了。
其实这个属性值和View在当前framePosition没有直接联系的属性值。
是不是很奇怪,的确如此,那么这玩意儿到底是干嘛的呢???
在这里,我们需要改变我们的想法,那就是KeyCycle里面设置的一切都是画出来的「波形图」,因此,这些参数的设置是为了修改波形图的具体形状。
<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最后形成的波形图是这样的。
每一个都可以发现framePosition属性值是绘制波形图的峰值。
在此基础上,waveOffset很容易理解,它的作用是给予framePosition的当前value增加一个初始值,也是为了修改波形。
要干嘛
你说KeyCycle这玩意儿整这么复杂,到底有什么用呢??
我们有了KeyFrame,可用于添加中间态关键帧,因此也需要KeyCycle干嘛呢?
说到这里,我不得不提一下Monotonic Spline(单调采样),通常关键帧插值算法采用单调采样,但不能实现曲线的平滑过渡,如下图中的绿色曲线,使用单调采样,变成以下曲线,过渡将非常僵硬。
所以为了使曲线顺利过渡,KeyCycle使用的是Typical Spline(特征采样),就像上图中的紫色曲线一样,四点顺利连接。
如果只是为了让曲线顺利过渡,那你就太小看了KeyCycle不得不说,外国人做的这些事情,总能在你觉得无关紧要的地方做得很深。
KeyCycle的核心在于波形,而波是什么呢?
上图表示sin和cos也有几何意义sin和cos的源。
说句不像傅里叶变换的话,我们可以将一个View的曲线运动,拆解成多个不同波形运动的叠加。
例如我们对一个View的translationX同时设置sin和cos的KeyCycle,最终形成的运动轨迹,就是一个圆形!
所以,由此及彼,我们可以复合多个属性的同时,通过不同的波形叠加,实现任何你想要的运动轨迹!这TM就牛逼了啊,简直就是傅里叶变换在Android动画中的实现了。
在CycleEditor中,有一些自带的Demo,可以让你充分的了解这个思想,例如下面这个例子。
太复杂了是吗?
CustomWave shape in keyCycle
CL2.1之后,motion:waveShape除了之前定义的sin、cos、bounce这些预设曲线外,你还可以设置自定义的波形曲线,定义方式如下所示。
<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的整体性控制
< END >
作者:徐宜生