模擬自然動畫的精髓——TimeInterpolator與TypeEvaluator
模擬自然動畫的精髓——TimeInterpolator與TypeEvaluator
通過屬性動畫,我們可以模擬各種屬性的動畫效果,但對于這些屬性來說,動畫變化的速率和范圍,是實現一個更加『真實、自然』的動畫的基礎,這兩件事情,就是通過TimeInterpolator與TypeEvaluator來實現的。
TimeInterpolator與TypeEvaluator共同作用在ValueAnimator上,通過復合的方式產生最后的數據,這也就是數學上的『復合函數』,TimeInterpolator控制在何時取值,而TypeEvaluator控制在當前時間點需要取多少值。
由于這里涉及到兩個變量,所以,這里我們通常使用『控制變量法』來進行這兩個屬性的研究,因為通常情況下,這兩個屬性的作用效果是殊途同歸的。
TimeInterpolator
首先,我們研究TimeInterpolator,所以,將TypeEvaluator設置為默認,不產生任何修改。
TimeInterpolator,中文常常翻譯成插值器。一個最簡單的屬性動畫,示例如下:
ObjectAnimator animator = ObjectAnimator.ofFloat(mTextView, "translationX", 0, mDistance);
animator.setDuration(mDuration);
animator.setInterpolator(new BounceInterpolator());
animator.start();
通過setInterpolator方法,可以給Animator設置插值器,默認的插值器是AccelerateDecelerateInterpolator,即加速減速插值器。
理解TimeInterpolator的作用原理
TimeInterpolator是作用在時間參數上,例如我們有一個動畫,時間從0到1,取值也從0到1,我們通過下面三條曲線來看同一時間點,取到的數值的不同。
當時間取0.5時,我們對應的y=x這條曲線,取出的是0.5,y=sqrt(x)這條曲線,取出的是0.25,y=x^2 這條曲線,取出的是0.7。也就是說,同一個真實的時間節點0.5,我們通過設置不同的函數曲線,取出了不同的數值,那么TimeInterpolator正是通過這種方式,來對時間參數進行修改,即,真實的時間0.5,對于其它兩個函數,分別取出了模擬時間0.25和0.7所對應的值,從而達到了『篡改』時間的目的。
Android中的TimeInterpolator
Android中已經給我們實現了很多TimeInterpolator,例如前面我們舉的例子——AccelerateDecelerateInterpolator。我們打開AccelerateDecelerateInterpolator的源碼。
其中關鍵的就是那行數學公式——(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f。我們來繪制下這個公式對應的曲線圖(這里input的取值范圍是0到1)。
在[0,1]區間內,就是我們的加速減速插值器了,結合字面意義很好理解。
那么在Android中,系統還給我們提供了非常多的TimeInterpolator,例如:AccelerateDecelerateInterpolator, AccelerateInterpolator, AnticipateInterpolator, AnticipateOvershootInterpolator, BounceInterpolator, CycleInterpolator, DecelerateInterpolator, LinearInterpolator, OvershootInterpolator, PathInterpolator。
大家可以通過API文檔來找到這些插值器的定義,同時,通過源碼來查看他們使用的數學公式。
自定義TimeInterpolator
自定義TimeInterpolator非常簡單,我們參考系統自帶的TimeInterpolator就可以實現了,即實現Interpolator接口和getInterpolation方法即可,例如:
package com.xys.naturalanim.views.interpolator;
import android.view.animation.Interpolator;
public class CustomInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
return (float) Math.sin((input) * Math.PI * 0.5F);
}
}</code></pre>
其重點就是實現getInterpolation方法中的數學公式。
TypeEvaluator
TypeEvaluator通常被翻譯成估值器,在理解了TimeInterpolator之后,再理解TypeEvaluator就很簡單了,一個系統自帶的簡單TypeEvaluator如下:

可見,它和TimeInterpolator基本一樣,只不過實現的公式的參數不一樣,但簡單的換算一下,就通用了,例如:
if (mInterpolator != null) {
for (int i = 0; i < mViewWidth; i++) {
mPath.lineTo(i, mViewHeight - mInterpolator.getInterpolation(i * 1.0F / mViewHeight) * mViewHeight);
}
} else {
for (int i = 0; i < mViewWidth; i++) {
mPath.lineTo(i, mViewHeight - (Integer) mTypeEvaluator.evaluate(i * 1.0F / mViewHeight, 0, mViewHeight));
}
}
但是它們還是有一些細小的區別的,后面再細說,簡單的概括,就是:
TimeInterpolator控制動畫的速度,而TypeEvaluator控制動畫的值,他們可以共同作用,也可以單獨作用(讓另一個使用默認值)。
實際上,TypeEvaluator中的一個參數fraction,就是『復合函數』中TimeInterpolator計算的結果。即fraction=getInterpolation()。
自定義TypeEvaluator
這里首先講一下TypeEvaluator的自定義,那么為什么要加呢,這是因為,這種方式限定了TypeEvaluator的類型是Number,那么這種就和TimeInterpolator幾乎可以完全轉化了,他們的目的都是通過提供的參數來完成曲線的繪制,從而實現對動畫運動的控制。而TimeInterpolator只有一個參數,實現起來更加的簡單,所以,大部分時候,我們都通過TimeInterpolator來實現這種運動曲線的模擬,所以,TypeEvaluator就這樣沒落了。
但是,不要以為TypeEvaluator就這樣沒用了,我們在小標題中也寫了,是類型的TypeEvaluator可以進行轉換,而TypeEvaluator實際上還有很多其它類型,在動畫的坐標控制上,有奇效。
TypeEvaluator控制點的坐標
前面我們說了,Float類型的TypeEvaluator和TimeInterpolator基本是一樣的,但TypeEvaluator并不只有Float這樣一種,它有一種用的比較多的特性,就是通過TypeEvaluator來對運動坐標進行修改,將原本的直線坐標修改成曲線坐標,它通常會與ValueAnimator進行配合使用,例如下面的這個例子:

這種實現曲線運動的方式,就是通過TypeEvaluator來進行實現的,其中核心原理,就是通過Bezier曲線的De Casteljau算法計算出具體的點坐標,并設置給TypeEvaluator,代碼如下所示。
public class BezierEvaluator implements TypeEvaluator<PointF> {
private PointF mControlPoint;
public BezierEvaluator(PointF controlPoint) {
this.mControlPoint = controlPoint;
}
@Override
public PointF evaluate(float t, PointF startValue, PointF endValue) {
return BezierUtil.CalculateBezierPointForQuadratic(t, startValue, mControlPoint, endValue);
}
}</code></pre>
Bezier的計算公式如下所示。
/**
- B(t) = (1 - t)^2 P0 + 2t (1 - t) P1 + t^2 P2, t ∈ [0,1]
*
- @param t 曲線長度比例
- @param p0 起始點
- @param p1 控制點
- @param p2 終止點
- @return t對應的點
/
public static PointF CalculateBezierPointForQuadratic(float t, PointF p0, PointF p1, PointF p2) {
PointF point = new PointF();
float temp = 1 - t;
point.x = temp temp p0.x + 2 t temp p1.x + t t p2.x;
point.y = temp temp p0.y + 2 t temp p1.y + t t * p2.y;
return point;
}</code></pre>
所以,綜上所述,在作動畫速率曲線控制的時候,使用TimeInterpolator即可,如果要改變點的坐標,就可以使用TypeEvaluator。
自然動畫
在了解了TimeInterpolator和TypeEvaluator之后,我們就可以來了解下動畫展現的優化方式了,普通的動畫默認以線性的方式展現,但帶來的后果就是動畫效果的『僵硬』,動畫本來是模擬兩個狀態的過渡過程的,這個在自然界中是『自然、流暢』的,所以,我們不能通過線性的數據變化來模擬自然動畫,這就需要使用TimeInterpolator和TypeEvaluator來設計動畫曲線了,通過它們來控制動畫的實現過程,從而實現動畫的展示,這就是我們來實現自然動畫的的基本方式。
緩動函數
既然線性的動畫曲線無法滿足我們的動畫模擬需求,那么就需要通過一定的數學公式來改變這些動畫曲線,值得慶幸的是,這些事情有人幫我們做過了,有人專門設計了這樣一些動畫的曲線庫。

就是這樣一些緩動函數庫,讓我們在設計動畫的時候,可以作更加真實的模擬。同時,你也可以設計自己的曲線函數,下面這個網站,就可以實現這樣的模擬。
自然動畫的模擬演示
在各位前輩的肩膀上,我這里擼了一個演示的Demo庫,界面如圖。

這里主要有幾個功能:
-
可以選擇不同的TimeInterpolator
-
可以選擇要演示的動畫效果,包括位移、縮放、旋轉、透明度
-
演示包含兩個View,上面的是設置對應動畫模擬效果的View,下面的是對照的線性效果的View
一個動態圖簡單的了解下:
