鏈接
歡迎大家訪問我的個人網(wǎng)站四度空間
Fuse?
很多時候泵三,設(shè)計師設(shè)計一個特效動畫出來磅崭,用靜態(tài)的UE是很難向開發(fā)者描述動效的具體樣式的垫竞,于是需要借助一些UX交互設(shè)計工具缓屠。
當前用的較多的UX交互設(shè)計工具是Fuse軟件了畜普,F(xiàn)use軟件要求設(shè)計師懂一些Html語言和JS語言語言期丰,Html寫UI,JS跑邏輯吃挑,大大提高溝通效率钝荡,同時會省卻開發(fā)者很多時間,因為在設(shè)計者寫好動畫特效之后舶衬,直接就可以通過Fuse軟件導(dǎo)出iOS或Android原型埠通,直接發(fā)布都可以,就拿iOS舉例逛犹,導(dǎo)出的Xcode工程端辱,確實運行沒有問題梁剔,但是內(nèi)存超大,而且顯得特別冗余舞蔽,因此如果把Fuse導(dǎo)出的這個工程直接融合進項目之中實在不劃算荣病。
開發(fā)者自己來寫動畫特效唯一的問題就是如何保證開發(fā)者自己寫的動畫特效跟設(shè)計師想要的動畫特效是完全一致的,一個動畫就主要兩方面渗柿,一是UI二是特效个盆,UI自然不在話下,只要參數(shù)一致朵栖,肯定是滿足設(shè)計者要求的颊亮,關(guān)鍵是特效。這就不得不引入一個影響動畫特效的關(guān)鍵因素緩動函數(shù)
混槐。
緩動函數(shù)编兄,顧名思義,就是決定動畫效果運動快慢的函數(shù)声登。
詳細說就是定義不同曲線的數(shù)據(jù)公式狠鸳,描述每一幀動畫回調(diào)時需要改變的圖形參數(shù)屬性的幅度,從而達到均勻悯嗓、先快后慢件舵、先慢后快,甚至先超出起始值和結(jié)束值再慢慢恢復(fù)的各種動畫特效脯厨。
因為大多數(shù)動畫特效都是模仿現(xiàn)實生活中的情形铅祸,時而加速時而減速。動畫特效直接受到緩動函數(shù)的影響合武,自然可以通過設(shè)置緩動函數(shù)進而實現(xiàn)不同的動畫效果临梗。設(shè)計師使用Fuse軟件設(shè)計出動畫特效時,里面必不可少的步驟就是設(shè)置UI動畫的緩動函數(shù)類型稼跳,當然Css的緩動函數(shù)名稱跟Java或OC的緩動函數(shù)名稱有些不同盟庞,看圖就一目了然,只要知道設(shè)計師使用的緩動函數(shù)汤善,找到相應(yīng)的坐標圖什猖,就順勢找出了OC中對應(yīng)的緩動函數(shù)名稱。
典型的線性kCAMediaTimingFunctionLinear红淡、漸快kCAMediaTimingFunctionEaseIn不狮、漸慢kCAMediaTimingFunctionEaseOut、先快后慢kCAMediaTimingFunctionEaseInEaseOut都是動畫本身就自帶的函數(shù)類型在旱,這也是最簡單的動畫特效摇零。不是我們重點討論的內(nèi)容,我們重點討論設(shè)計師設(shè)計的復(fù)雜緩動函數(shù)構(gòu)建的動畫特效颈渊。
復(fù)雜緩動函數(shù)曲線通常都是需要開發(fā)者自定義遂黍,iOS的原生動畫框架對自定義緩動函數(shù)曲線支持不是很好终佛,所以這也是FaceBook開源的POP動畫框架大受歡迎的原因。
POP動畫框架就是通過改變視圖模型層Model Layer狀態(tài)來實現(xiàn)動畫雾家,原生Core Animation的是通過改變渲染層Render Layer實現(xiàn)動畫铃彰,所以Core Animation動畫會在動畫結(jié)束后回到起始位置和無法實現(xiàn)UILable的Text屬性動畫效果,POP動畫框架不止于此芯咧,更可以創(chuàng)建動畫框架沒有的時間緩動函數(shù)曲線進而構(gòu)建特別的動畫特效牙捉,本質(zhì)上來說,POP動畫框架的本質(zhì)是CADisplayLink定時器(屏幕刷新率:60幀/S)每觸發(fā)一次屏幕刷新的方法就執(zhí)行一幀動畫敬飒,根據(jù)設(shè)定的緩動函數(shù)Time Function來計算每一幀動畫的插值邪铲,進而賦值給視圖或圖層的屬性(Center、Frame无拗、Text)形成動畫效果带到。
對于POP動畫框架來說,設(shè)置緩動函數(shù)曲線主要兩種方法英染,具體哪一種方法視緩動函數(shù)曲線的復(fù)雜度而定揽惹。
- 第一種方法較為簡單,只適用于通過兩個構(gòu)建點就能構(gòu)建的緩動函數(shù)曲線四康、獲取兩個構(gòu)建點的坐標搪搏,作為參數(shù)傳入POP動畫框架的API。
- 第二種方法就需要理解POP的原理才能完成闪金,POPCustomAnimation類的Block代碼塊在CADisplayLink定時器每一次觸發(fā)方法是都會調(diào)用疯溺,前提必須知道緩動函數(shù)曲線對應(yīng)的C語言計算函數(shù)JS查詢表,每一幀動畫都需要計算一次哎垦,就是說囱嫩,緩動函數(shù)曲線的每一個坐標點都是通過C語言計算函數(shù)實時計算構(gòu)建出來的,計算頻率與CADisplayLink一致漏设,這樣無論多復(fù)雜的緩動函數(shù)曲線都可以更具具體的函數(shù)實時構(gòu)建出來挠说。因為本質(zhì)定時器,自然POP動畫是在主線層執(zhí)行的愿题,消耗CPU較嚴重,所以如果只是簡單的動畫使用分線程執(zhí)行的UIView的Block動畫是一個更好的選擇蛙奖。
動畫分類潘酗?
- 基本動畫:線性
kCAMediaTimingFunctionLinear
、漸快kCAMediaTimingFunctionEaseIn
雁仲、漸慢kCAMediaTimingFunctionEaseOut
仔夺、先快后慢kCAMediaTimingFunctionEaseInEaseOut
共四種時間緩動函數(shù)。 - 彈簧動畫:
springBounciness
[0-20]彈力越大則震動幅度越大攒砖、springSpeed
[0-20]速度越大則動畫結(jié)束越快缸兔、dynamicsFriction
摩擦日裙、dynamicsMass
質(zhì)量,動畫持續(xù)的時間和時間的緩動函數(shù)由以上幾個參數(shù)決定惰蜜。 - 阻尼動畫:
deceleration
衰減系數(shù)剎車昂拂,動畫持續(xù)的時間和時間的緩動函數(shù)由衰減系數(shù)決定 - 自定義動畫
自定義動畫?
方法一:
- 自定義動畫第一步需要自定義時間的緩動函數(shù)曲線抛猖。
- 實現(xiàn)POPCustomAnimation類的Block代碼塊參數(shù)格侯。每次CADisplayLink定時器觸發(fā)時也就是每一幀動畫都會調(diào)用我們定義好的Block。
- 記錄動畫開始時的基準時間财著、X軸的時間進度[0-1]百分比联四、調(diào)用緩動函數(shù)ElasticEaseOut對應(yīng)的C語言函數(shù)(參照JS函數(shù)表)計算出Y值進度[0-1]百分比、根據(jù)緩動函數(shù)輸出的Y值計算動畫插值賦值給UI對象撑教、判斷如果Y值進度小于1繼續(xù)動畫
方法二:(有局限朝墩,只適用于較簡單的動畫)
- 移動兩個坐標點隨意構(gòu)建緩動函數(shù)曲線
- 獲取兩個坐標點坐標。為什么POP動畫的自定義換動函數(shù)API只能輸入兩個坐標點伟姐,原來貝塞爾曲線真的是通過兩個坐標點構(gòu)建的(當然這里排除(0,0)和(1,1)這兩個坐標)收苏。在動畫執(zhí)行之前就把緩動函數(shù)構(gòu)建完成,這樣就不用每一幀動畫都計算一次緩動函數(shù)下一坐標點了玫镐,當然在動畫開始之前就構(gòu)建出完整的緩動函數(shù)曲線在精度是比不上每一幀都調(diào)用曲線的C語言函數(shù)這個方法的倒戏。
- 自定義緩動函數(shù)完成之后,更可以把自定義曲線的名字和兩個坐標點以Json數(shù)據(jù)的格式導(dǎo)入到庫之中恐似,不僅僅起到保存的功能喲杜跷,更可以去除來加以比較,進行微調(diào)矫夷,簡直棒到?jīng)]朋友呀葛闷!
貝塞爾曲線的數(shù)學(xué)公式參考實現(xiàn)(JS版)
(function(factory){
if(typeof define === "function" && define.amd){
define(['jquery'],function($){
return factory($);
});
} else if(typeof module === "object" && typeof module.exports === "object"){
exports = factory(require('jquery'));
} else {
factory(jQuery);
}
})
(function($){
$.easing['jswing']= $.easing['swing'];
var pow = Math.pow,
sqrt = Math.sqrt,
sin = Math.sin,
cos = Math.cos,
PI = Math.PI,
c1 = 1.70158,
c2 = c1 * 1.525,
c3 = c1 + 1,
c4 =(2 * PI)/ 3,
c5 =(2 * PI)/ 4.5;
function bounceOut(x){
var n1 = 7.5625,
d1 = 2.75;
if(x < 1/d1){
return n1*x*x;
} else if(x < 2/d1){
return n1*(x-=(1.5/d1))*x + .75;
} else if(x < 2.5/d1){
return n1*(x-=(2.25/d1))*x + .9375;
} else {
return n1*(x-=(2.625/d1))*x + .984375;
}
}
$.extend($.easing,
{
def: 'easeOutQuad',
swing: function(x){
return $.easing[$.easing.def](x);
},
easeInQuad: function(x){
return x * x;
},
easeOutQuad: function(x){
return 1 -(1 - x)*(1 - x);
},
easeInOutQuad: function(x){
return x < 0.5?
2 * x * x :
1 - pow(-2 * x + 2,2)/ 2;
},
easeInCubic: function(x){
return x * x * x;
},
easeOutCubic: function(x){
return 1 - pow(1 - x,3);
},
easeInOutCubic: function(x){
return x < 0.5?
4 * x * x * x :
1 - pow(-2 * x + 2,3)/ 2;
},
easeInQuart: function(x){
return x * x * x * x;
},
easeOutQuart: function(x){
return 1 - pow(1 - x,4);
},
easeInOutQuart: function(x){
return x < 0.5?
8 * x * x * x * x :
1 - pow(-2 * x + 2,4)/ 2;
},
easeInQuint: function(x){
return x * x * x * x * x;
},
easeOutQuint: function(x){
return 1 - pow(1 - x,5);
},
easeInOutQuint: function(x){
return x < 0.5?
16 * x * x * x * x * x :
1 - pow(-2 * x + 2,5)/ 2;
},
easeInSine: function(x){
return 1 - cos(x * PI/2);
},
easeOutSine: function(x){
return sin(x * PI/2);
},
easeInOutSine: function(x){
return -(cos(PI * x)- 1)/ 2;
},
easeInExpo: function(x){
return x === 0?0 : pow(2,10 * x - 10);
},
easeOutExpo: function(x){
return x === 1?1 : 1 - pow(2,-10 * x);
},
easeInOutExpo: function(x){
return x === 0?0 : x === 1?1 : x < 0.5?
pow(2,20 * x - 10)/ 2 :
(2 - pow(2,-20 * x + 10))/ 2;
},
easeInCirc: function(x){
return 1 - sqrt(1 - pow(x,2));
},
easeOutCirc: function(x){
return sqrt(1 - pow(x - 1,2));
},
easeInOutCirc: function(x){
return x < 0.5?
(1 - sqrt(1 - pow(2 * x,2)))/ 2 :
(sqrt(1 - pow(-2 * x + 2,2))+ 1)/ 2;
},
easeInElastic: function(x){
return x === 0?0 : x === 1?1 :
-pow(2,10 * x - 10)* sin((x * 10 - 10.75)* c4);
},
easeOutElastic: function(x){
return x === 0?0 : x === 1?1 :
pow(2,-10 * x)* sin((x * 10 - 0.75)* c4)+ 1;
},
easeInOutElastic: function(x){
return x === 0?0 : x === 1?1 : x < 0.5?
-(pow(2,20 * x - 10)* sin((20 * x - 11.125)* c5))/ 2 :
pow(2,-20 * x + 10)* sin((20 * x - 11.125)* c5)/ 2 + 1;
},
easeInBack: function(x){
return c3 * x * x * x - c1 * x * x;
},
easeOutBack: function(x){
return 1 + c3 * pow(x - 1,3)+ c1 * pow(x - 1,2);
},
easeInOutBack: function(x){
return x < 0.5?
(pow(2 * x,2)*((c2 + 1)* 2 * x - c2))/ 2 :
(pow(2 * x - 2,2)*((c2 + 1)*(x * 2 - 2)+ c2)+ 2)/ 2;
},
easeInBounce: function(x){
return 1 - bounceOut(1 - x);
},
easeOutBounce: bounceOut,
easeInOutBounce: function(x){
return x < 0.5?
(1 - bounceOut(1 - 2 * x))/ 2 :
(1 + bounceOut(2 * x - 1))/ 2;
}
});
});