當數學遇上動畫(3)
當數學遇上動畫:講述 ValueAnimator、TypeEvaluator和TimeInterpolator之間的恩恩怨怨(3)
上一節我們得到一個重要的結論,藉助TimeInterpolator
或者TypeEvaluator
“單獨” 來控制動畫所產生的動畫效果殊途同歸!
此外,上一節結尾我們還說到,專案AnimationEasingFunctions和專案EaseInterpolator本質上是差不多的,都是定義了一些動畫效果對應的函式曲線。前者是將其封裝成了TypeEvaluator
,後者是將其封裝成了Interpolator
!
這一節我們來研究下這些函式曲線。
1 緩動函式曲線
下圖顯示了常見的這些函式曲線,到底這些函式曲線都是什麼鬼呢?
這些函式曲線最早是由Robert Penner提出來用於實現補間動畫的"Penner easing functions"
,這些曲線主要分成10類,包括"BACK", "BOUNCE", "CIRCULAR", "ELASTIC", "EXPO", "QUAD", "CUBIC", "QUART", "QUINT", "SINE"
,每一類下面都有緩動進入、緩動退出以及緩動進入和退出三種效果,所以共有30個。這些效果對照著函式曲線來看其實也挺好理解,"QUAD", "CUBIC", "QUART", "QUINT"
分別對應著二次、三次、四次以及五次曲線,"SINE"
對應正弦函式曲線,"EXPO"
對應指數函式曲線等等。其中"BACK"
"ELASTIC"
有上衝和下衝的效果。
這些版本的實現都是4個引數的,分別是起始值b
、數值間隔c
(結束值-起始值)、當前時間t
、時間間隔d
。
12345678910111213141516171819202122232425 | //不帶緩動,也就是前面說的“線性”估值器functionnoEasing(t,b,c,d){returnc*(t/d)+b;}//帶緩動效果,例如二次曲線形式easeInQuad:function(t,b,c,d){//緩動進入returnc*(t/=d)*t+b;},easeOutQuad:function(t,b,c,d){//緩動退出return-c*(t/=d)*(t-2)+b;},easeInOutQuad:function(t,b,c,d){//緩動進入和退出if((t/=d/2)<1)returnc/2*t*t+b;return-c/2*((--t)*(t-2)-1)+b;},那為什麼與之殊途同歸的EaseInterpolator是1個引數的呢?//QuadInOut InterpolatorpublicfloatgetInterpolation(floatinput){if((input/=0.5f)<1){return0.5f*input*input;}return-0.5f*((--input)*(input-2)-1);} |
這是因為當Interpolator
傳入到後面的TypeEvaluator
的時候就有了起始值、結束值以及時間間隔(時間間隔定義在緩動函式內部,只有部分緩動函式需要這個引數)這3個引數,可以參考下面的程式碼來理解,所以說,它們在本質上還是一樣的!
12 | fraction=getInterpolation(input)==>這種1個引數形式其實也可以等效於easingfunction(currentTime,0,1,totalTime)value=evaluate(fraction,startValue,endValue)=startValue+fraction*(endValue-startValue) |
2 One more thing
看到這裡的話,我們就會想啦,如果我們把函式曲線抽象出來,然後再提供相應的轉換方法,使其輕輕鬆鬆地轉換成Interpolator
和TypeEvaluator
的話,如此,豈不善哉?
所以,我就站在眾多巨人們的肩膀上,寫了一個新專案Yava,專案程式碼非常簡單,而且程式碼很少只有4個重要的類,它實現的功能就是將抽象的函式曲線輕鬆轉換成立即可用的Interpolator
和TypeEvaluator
,並且提供了常見的30個緩動函式(Easing Functions)的實現,它們既可以當做Interpolator
來用,又可以當做TypeEvaluator
來用,非常方便。
這裡我直接把這4個重要類的程式碼貼出來吧。
(1) IFunction
介面
123456 | /** * 函式介面:給定輸入,得到輸出 */publicinterfaceIFunction{floatgetValue(floatinput);} |
(2)AbstractFunction
抽象類
123456789101112131415 | /** * 抽象函式實現,既可以當做簡單函式使用,也可以當做Interpolator或者TypeEvaluator去用於製作動畫 */publicabstractclassAbstractFunction implementsIFunction,Interpolator,TypeEvaluator<Float>{@OverridepublicfloatgetInterpolation(floatinput){returngetValue(input);}@OverridepublicFloatevaluate(floatfraction,FloatstartValue,FloatendValue){returnstartValue+getValue(fraction)*(endValue-startValue);}} |
(3)Functions
類
1234567891011121314 | /** * 工具類,將自定義的函式快速封裝成AbstractFunction */classFunctions{publicstaticAbstractFunction with(finalIFunction function){returnnewAbstractFunction(){@OverridepublicfloatgetValue(floatinput){returnfunction.getValue(input);}};}} |
(4)EasingFunction
列舉:包含了30個常見的緩動函式
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 | /** * 常見的30個緩動函式的實現 */publicenumEasingFunction implementsIFunction,Interpolator,TypeEvaluator<Float>{/* ------------------------------------------------------------------------------------------- *//* BACK /* ------------------------------------------------------------------------------------------- */BACK_IN{@OverridepublicfloatgetValue(floatinput){returninput*input*((1.70158f+1)*input-1.70158f);}},BACK_OUT{@OverridepublicfloatgetValue(floatinput){return((input=input-1)*input*((1.70158f+1)*input+1.70158f)+1);}},BACK_INOUT{@OverridepublicfloatgetValue(floatinput){floats=1.70158f;if((input*=2)<1){return0.5f*(input*input*(((s*=(1.525f))+1)*input-s));}return0.5f*((input-=2)*input*(((s*=(1.525f))+1)*input+s)+2);}},//other easing functions ......//如果這個function在求值的時候需要duration作為引數的話,那麼可以通過setDuration來設定,否則使用預設值privatefloatduration=1000f;//目前只有ELASTIC***這三個是需要duration的,其他的都不需要publicfloatgetDuration(){returnduration;}publicEasingFunction setDuration(floatduration){this.duration=duration;returnthis;}//將Function當做Interpolator使用,預設的實現,不需要列舉元素去重新實現@OverridepublicfloatgetInterpolation(floatinput){returngetValue(input);}//將Function當做TypeEvaluator使用,預設的實現,不需要列舉元素去重新實現@OverridepublicFloatevaluate(floatfraction,FloatstartValue,FloatendValue){returnstartValue+getValue(fraction)*(endValue-startValue);}//幾個數學常量publicstaticfinalfloatPI=(float)Math.PI;publicstaticfloatTWO_PI=PI*2.0f;publicstaticfloatHALF_PI=PI*0.5f;} |
這個專案的緩動函式的實現參考自EaseInterpolator中的實現,但是這個專案的程式碼和EaseInterpolator以及AnimationEasingFunctions這兩個專案都完全不一樣,非常簡單易懂,既保留了原有專案應有的功能,同時為專案的使用場景提供了更多的可能,任何你想使用Interpolator
或者TypeEvaluator
都能使用它。
舉個例子,以上一節中的彈跳動畫效果為例,現在可以直接使用EasingFunction.BOUNCE_OUT
作為Interpolator
或者TypeEvaluator
來使用:
第一種方式:使用線性插值器和自定義的TypeEvaluator
Java12345678 | ObjectAnimator animator1=newObjectAnimator();animator1.setTarget(textView1);animator1.setPropertyName("translationY");animator1.setFloatValues(0f,-100f);animator1.setDuration(1000);animator1.setInterpolator(newLinearInterpolator() |