5

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

 2 years ago
source link: https://xuyisheng.top/constraintlayout2_keycycles/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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转换为波形。

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,代码如下所示。

<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里面设置的一切东西,都是为了画出「波形图」,所以,这些参数的设置,就是为了修改波形图的具体形状。

<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这些预设曲线外,你还可以设置自定义的波形曲线,定义方式如下所示。

<KeyCycle motion:waveShape=”spline(0.0, 1.0, -1.0, 0)” />

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

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

向大家推荐下我的网站 https://xuyisheng.top/ 专注 Android-Kotlin-Flutter 欢迎大家访问


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK