這是一篇記載幾個(gè)關(guān)鍵點(diǎn)以備查詢的文章
項(xiàng)目3.0版本告一段落殃恒,在這個(gè)版本的設(shè)計(jì)與制作質(zhì)量相較2.0版本有了很大提高。為此 UI 的同事設(shè)計(jì)了許多動(dòng)效。平時(shí)雖然經(jīng)忱胩疲看這方面的文章病附,但是真正做的時(shí)候還是踩了許多坑,這也印證了“紙上得來(lái)終覺(jué)淺亥鬓,絕知此事要躬行”完沪。在此,寫下這篇文章記錄一下以備查詢嵌戈。
CoreAnimation 繪圖坐標(biāo)系的問(wèn)題
CoreAnimation 默認(rèn)的坐標(biāo)系原點(diǎn)是在屏幕左下方覆积,x 和 y 軸的正方向分別指向屏幕上方和右方。
例如這段代碼
- (CGMutablePathRef)createAnalyzePicPathWithPicRect:(CGRect)rect{
CGFloat scaleRate = [UIScreen mainScreen].scale;
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, rect.origin.x + rect.size.width/2, rect.origin.y + rect.size.height/2);
transform = CGAffineTransformScale(transform, 1/scaleRate, -1/scaleRate);
CGMutablePathRef path = CGPathCreateMutable();
//逆時(shí)針去點(diǎn)
//point1
CGFloat radius1 = (arc4random()%50+51)/100.0 * MIN(rect.size.width, rect.size.height);
CGPathMoveToPoint(path, &transform, radius1, 0);
//point2
CGFloat radius2 = (arc4random()%50+51)/100.0 * MIN(rect.size.width, rect.size.height);
CGPathAddLineToPoint(path, &transform,radius2 * cos(M_PI_4) ,radius2 * sin(M_PI_4) );
//point3
CGFloat radius3 = (arc4random()%50+51)/100.0 * MIN(rect.size.width, rect.size.height);
CGPathAddLineToPoint(path, &transform, 0, radius3);
//point4
CGFloat radius4 = (arc4random()%50+51)/100.0 * MIN(rect.size.width, rect.size.height);
CGPathAddLineToPoint(path, &transform,radius4 * cos(M_PI_4 * 3) ,radius4 * sin(M_PI_4 * 3) );
//point5
CGFloat radius5 = (arc4random()%50+51)/100.0 * MIN(rect.size.width, rect.size.height);
CGPathAddLineToPoint(path, &transform, -radius5, 0);
//point6
CGFloat radius6 = (arc4random()%50+51)/100.0 * MIN(rect.size.width, rect.size.height);
CGPathAddLineToPoint(path, &transform,radius6 * cos(M_PI_4 * 5) ,radius6 * sin(M_PI_4 * 5) );
//point7
CGFloat radius7 = (arc4random()%50+51)/100.0 * MIN(rect.size.width, rect.size.height);
CGPathAddLineToPoint(path, &transform, 0, -radius7);
//point8
CGFloat radius8 = (arc4random()%50+51)/100.0 * MIN(rect.size.width, rect.size.height);
CGPathAddLineToPoint(path, &transform,radius8 * cos(M_PI_4 * 7) ,radius8 * sin(M_PI_4 * 7) );
//close
CGPathCloseSubpath(path);
return path;
}
注意這一句
CGMutablePathRef path = CGPathCreateMutable();
這個(gè) CGPth 是直接創(chuàng)建出來(lái)的熟呛,因此沒(méi)有應(yīng)用 transform宽档,此時(shí)繪制六邊形的時(shí)候,使用的坐標(biāo)系就是 CoreAnimation 的坐標(biāo)系庵朝。計(jì)算角度的時(shí)候注意就可以了吗冤。
但是,如果 CGContext 是用 UIKit 框架的方法獲得的九府,例如:
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
那么椎瘟,這些方法會(huì)在 CGContext 上面應(yīng)用一個(gè) transform,讓坐標(biāo)系的原點(diǎn)變成屏幕左上角昔逗,x 軸與 y 軸的正方向分別指向屏幕下方和右方降传,繪圖的時(shí)候相應(yīng)的計(jì)算也變一下就可以了。
CAKeyframeAnimation 畫動(dòng)畫時(shí)需要注意的幾個(gè)參數(shù)
CAKeyframeAnimation 是非常強(qiáng)大的勾怒。他主要有這樣幾個(gè)參數(shù):
- values
@property(nullable, copy) NSArray *values;
這里可以傳的東西與 CABasicAnimation 中能生成隱式動(dòng)畫的東西是一致的婆排。例如:position transform opacity 這些keypath 要求的 NSNumber。
也可以傳 CGImage 和 CGPath 等笔链。
下面分別是傳入 CGImage 和 CGPath 的例子:
這里可以看到段只,values 設(shè)置為不同的 CGImage 后,效果非常漂亮鉴扫。
這是一個(gè)傳入 CGPath 的例子赞枕,可以看到,中間紅色和藍(lán)色的分析圖的動(dòng)畫坪创,在關(guān)鍵幀之間自動(dòng)插入了補(bǔ)間動(dòng)畫炕婶。關(guān)鍵幀的 CGPath 就是用上面畫六邊形的代碼生成的。
- keytimes
@property(nullable, copy) NSArray<NSNumber *> *keyTimes;
這個(gè)參數(shù)沒(méi)有什么好介紹的莱预,就是文檔中所講的柠掂,keytimes 數(shù)組中元素的個(gè)數(shù)與 values 必須相同。同時(shí)依沮,第一個(gè)值必須是0涯贞,最后一個(gè)必須是1枪狂,中間的值需要介于0~1之間。
phoneContainerAnimation.values = @[(__bridge id)[UIImage imageNamed:@"guide_空-手機(jī)內(nèi)容"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_費(fèi)率1折-手機(jī)內(nèi)容"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_空-手機(jī)內(nèi)容"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_熱門專題-手機(jī)界面"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_空-手機(jī)內(nèi)容"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_基金超市-手機(jī)內(nèi)容"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_空-手機(jī)內(nèi)容"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_定投-手機(jī)界面"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_空-手機(jī)內(nèi)容"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_T+0+手機(jī)界面"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_空-手機(jī)內(nèi)容"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_智能-手機(jī)界面"].CGImage,(__bridge id)[UIImage imageNamed:@"guide_空-手機(jī)內(nèi)容"].CGImage];
phoneContainerAnimation.keyTimes = @[[NSNumber numberWithFloat:0],[NSNumber numberWithFloat:(1.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:(2.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:(3.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:(4.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:(5.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:(6.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:(7.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:(8.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:(9.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:(10.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:(11.0 * timeSlice/totalDuration)],[NSNumber numberWithFloat:1]];
- timingFunctions 和 calculationMode
如果 CAKeyframeAnmation 的 values 中有 n 項(xiàng)宋渔,那么 timingFunctions 必須有n-1個(gè)CAMediaTimingFunction對(duì)州疾。
它所描述的是關(guān)鍵幀到幀的變化速率。
kCAMediaTimingFunctionLinear
kCAMediaTimingFunctionDefault 這個(gè)曲線怎么形容呢?反正挺好看也挺好用的不是嗎
kCAMediaTimingFunctionEaseIn 慢-快
kCAMediaTimingFunctionEaseOut 快-慢
kCAMediaTimingFunctionEaseInEaseOut 慢-快-慢
銀行卡跳動(dòng)的動(dòng)畫使用了 timingFunctions 調(diào)節(jié)關(guān)鍵幀之間的變換速率皇拣,有點(diǎn)彈跳的感覺(jué)严蓖。
calculationMode 其主要針對(duì)的是每一幀的內(nèi)容為一個(gè)座標(biāo)點(diǎn)的情況,也就是對(duì)anchorPoint 和 position 進(jìn)行的動(dòng)畫.當(dāng)在平面座標(biāo)系中有多個(gè)離散的點(diǎn)的時(shí)候,可以是離散的,也可以直線相連后進(jìn)行插值計(jì)算,也可以使用圓滑的曲線將他們相連后進(jìn)行插值計(jì)算.calculationMode目前提供如下幾種模式:
kCAAnimationLinear calculationMode的默認(rèn)值,表示當(dāng)關(guān)鍵幀為座標(biāo)點(diǎn)的時(shí)候,關(guān)鍵幀之間直接直線相連進(jìn)行插值計(jì)算; kCAAnimationDiscrete 離散的,就是不進(jìn)行插值計(jì)算,所有關(guān)鍵幀直接逐個(gè)進(jìn)行顯示; kCAAnimationPaced 使得動(dòng)畫均勻進(jìn)行,而不是按keyTimes設(shè)置的或者按關(guān)鍵幀平分時(shí)間,此時(shí)keyTimes和timingFunctions無(wú)效; kCAAnimationCubic 對(duì)關(guān)鍵幀為座標(biāo)點(diǎn)的關(guān)鍵幀進(jìn)行圓滑曲線相連后插值計(jì)算,這里的主要目的是使得運(yùn)行的軌跡變得圓滑审磁;
kCAAnimationCubicPaced 看這個(gè)名字就知道和kCAAnimationCubic有一定聯(lián)系,其實(shí)就是在kCAAnimationCubic的基礎(chǔ)上使得動(dòng)畫運(yùn)行變得均勻,就是系統(tǒng)時(shí)間內(nèi)運(yùn)動(dòng)的距離相同,此時(shí)keyTimes以及timingFunctions也是無(wú)效的.
http://blog.csdn.net/u011700462/article/details/37540709
- removedOnCompletion 和 fillMode
眾所周知谈飒,CAPropertyAnimation 在添加到 CALayer 上的時(shí)候,會(huì)產(chǎn)生一份 copy态蒂,并且杭措,取 Animation 的時(shí)候也必須用 animation 的 key 來(lái)取。
在動(dòng)畫完成后钾恢,如果removedOnCompletion=YES手素,則會(huì)自動(dòng)的從 CALayer 上面移除動(dòng)畫。
我們也知道瘩蚪,CALayer 添加 CAPropertyAnimation 后泉懦,會(huì)將原 CALayer 隱藏,同時(shí)產(chǎn)生一個(gè)新的 CALayer 專門用來(lái)做動(dòng)畫疹瘦,等動(dòng)畫完成后崩哩,默認(rèn)情況下原 CALayer 會(huì)再次出現(xiàn)。例如言沐,對(duì) position 做動(dòng)畫邓嘹,控件移動(dòng)到最終位置后會(huì)突然回到初始位置。
如果想讓控件始終保持在最終位置险胰,可以設(shè)置 fillMode汹押。
kCAFillModeForwards //當(dāng)動(dòng)畫結(jié)束后,layer會(huì)一直保持著動(dòng)畫最后的狀態(tài).
kCAFillModeBackwards //這個(gè)和kCAFillModeForwards是相對(duì)的,就是在動(dòng)畫開(kāi)始前,你只要將動(dòng)畫加入了一個(gè)layer,layer便立即進(jìn)入動(dòng)畫的初始狀態(tài)并等待動(dòng)畫開(kāi)始.你可以這樣設(shè)定測(cè)試代碼,將一個(gè)動(dòng)畫加入一個(gè)layer的時(shí)候延遲5秒執(zhí)行.然后就會(huì)發(fā)現(xiàn)在動(dòng)畫沒(méi)有開(kāi)始的時(shí)候,只要?jiǎng)赢嫳患尤肓薼ayer,layer便處于動(dòng)畫初始狀態(tài).
kCAFillModeBoth
kCAFillModeRemoved
設(shè)置 fillMode 為 kCAFillModeForwards、kCAFillModeBoth,必須讓 removedOnCompletion 為 NO 才行起便。
http://www.cnblogs.com/xs514521/archive/2016/02/16/5192378.html
- autoreverses
這個(gè)屬性可以讓動(dòng)畫正向完成后再做反向動(dòng)畫棚贾。如果這個(gè)位 YES 那么 repeatCount 的 1 是指正向+反向。
repeatCount 可以與 fillMode 結(jié)合榆综。例如 repeatCount=1.5 fillMode = kCAFillModeForwards妙痹,那么動(dòng)畫是 fromValue-toValue-fromValue-toValue。
這個(gè)過(guò)程與下面談的時(shí)間控制息息相關(guān)鼻疮。
CALayer 和 CoreAnimation 中動(dòng)畫運(yùn)行時(shí)間的控制
CAAnimation 和 CALayer 類都實(shí)現(xiàn)了一個(gè)協(xié)議——CAMediaTiming细诸。這個(gè)協(xié)議中的方法可以幫我們控制動(dòng)畫的進(jìn)度。
每一個(gè) CALayer 都有自己的控制動(dòng)畫進(jìn)度的時(shí)間線陋守,父 layer 的時(shí)間線會(huì)影響到子 layer 的時(shí)間線震贵。
這也就是我們平時(shí)設(shè)置 CALayer 的 beginTime 時(shí),必須 CACurrentMediaTime() + 0.5 才行水评。
CALayer 的時(shí)間線可以使用 convertTime:fromLayer: convertTime:toLayer: 轉(zhuǎn)換猩系,就像在不同的 layer 中轉(zhuǎn)換坐標(biāo)一樣。
設(shè)置 CALayer 的 speed 屬性中燥,可以讓動(dòng)畫播放的速度成倍的改變寇甸,設(shè)置成 0 可以讓動(dòng)畫暫停,設(shè)置小于1可以讓動(dòng)畫后退播放疗涉。
設(shè)置 CALayer 的 beginTime 和 speed拿霉,會(huì)影響到 CALayer 以及其子 layer 的所有動(dòng)畫運(yùn)行速度。
當(dāng)然咱扣,也可以為 CoreAnimation 設(shè)置 beginTime 和 speed绽淘。如果 layer 上面有多個(gè) animation,這樣設(shè)置只會(huì)影響一個(gè) animation 的動(dòng)畫速度闹伪。然而沪铭,如果 CoreAnimation 已經(jīng)添加到 CALayer 上,就不能修改 beginTime 和 speed 了偏瓤,必須從 CALayer 上面移除后再添加才可以杀怠,直接修改會(huì)報(bào)錯(cuò)。
對(duì) beginTime 和 speed 的控制厅克,在平時(shí)的開(kāi)發(fā)中可能用到的不多赔退,但是系統(tǒng)提供的自定義 ViewController 轉(zhuǎn)場(chǎng)相關(guān)代碼中,底層就是使用這個(gè)特性實(shí)現(xiàn)的证舟。
CAAnimationGroup 的 duration autoreverses repeatCount 等會(huì)影響到添加到 group 中的 animation ,這些參數(shù)會(huì)覆蓋 animation 的這些參數(shù)硕旗。
其它幾個(gè)小技巧
1.CoreAnimation 是在子線程運(yùn)行的
動(dòng)畫的各個(gè)屬性都是在子線程計(jì)算的,如果需要在動(dòng)畫結(jié)束后精確的改變程序中的某些參數(shù)褪储,請(qǐng)使用 CAAnimationDelegate卵渴。使用 GCD 的 dispatch_after 來(lái)實(shí)現(xiàn)動(dòng)畫完成后進(jìn)行某些操作可能會(huì)出現(xiàn)操作發(fā)生的時(shí)間點(diǎn)與動(dòng)畫完成的時(shí)間點(diǎn)不完全一致的情況。
2.CGContextAddArc 的參數(shù)與坐標(biāo)系
CGContextAddArc(Context, CGFloat x , CGFloat y, CGFloat radius, CGFloat startAngle , CGFloat endAngle, int clockwise);
xy 圓弧原點(diǎn)坐標(biāo) radius 半徑 startAngle 和 endAngle 必須傳入弧度 colckwise 0是逆時(shí)針鲤竹,1是順時(shí)針浪读。圓弧會(huì)從 startAngle 與 radius 描述的坐標(biāo),按照 colckwise 規(guī)定的方向繪制到 endAngle 描述的坐標(biāo)辛藻,即使在數(shù)學(xué)上 startAngle 比 endAngle 大也不例外碘橘。
在 CoreGraphics 繪圖中,坐標(biāo)系 x 軸正方向向右吱肌,y 軸正方向向上痘拆。弧度0位于 x 軸的正方向上 pi/2 位于 y 軸的正方向上氮墨,與數(shù)學(xué)上的定義是一致的纺蛆。
如果 Context 是從 UIKit 的繪圖方法中取得的吐葵,比如 UIGraphicsImageContext 那么會(huì)應(yīng)用一個(gè) transform,這時(shí)再計(jì)算 CGContextAddArc 的參數(shù)也要做出相應(yīng)變化才行桥氏。
3.repeatCount = repeatDuration/duration
repeatCount 和 repeatDuration 不能同時(shí)使用温峭。
4.CoreAnimation 有個(gè)屬性 removedOnCompletion ,如果是 YES字支,那么動(dòng)畫過(guò)程中按 Home 鍵退到后臺(tái)凤藏,再進(jìn)入 app,動(dòng)畫就沒(méi)了堕伪。
5.有個(gè)挺好用的庫(kù) lottie 這個(gè)有 lottie-ios 和 lottie-android揖庄,可以把 After Effect 制作的動(dòng)畫直接導(dǎo)入 app 中,但是在管理動(dòng)畫播放進(jìn)度上面力不從心欠雌。如果遇到比較復(fù)雜的又與用戶操作沒(méi)有交互的動(dòng)畫(例如蹄梢,不需要用手勢(shì)控制動(dòng)畫的進(jìn)度)可以考慮用這個(gè)庫(kù)。
6.UIImageView 有個(gè) animationimages 屬性桨昙,可以直接播放序列圖检号,能夠起到與 lottie 相同的效果。但是 lottie 方式占用空間比較小蛙酪,效果也比較好齐苛,因?yàn)?lottie 可以使用 json 和矢量圖描述動(dòng)畫。
** 以上記錄了最近使用動(dòng)畫和繪圖相關(guān)方法時(shí)發(fā)現(xiàn)的一些技巧和遇到的一些問(wèn)題桂塞。這些點(diǎn)都可以搜索到更詳細(xì)的解釋說(shuō)明凹蜂,在此只是整理記錄一下,以后遇到相關(guān)問(wèn)題時(shí)阁危,可以從這些點(diǎn)出發(fā)去尋找相應(yīng)的知識(shí)解決問(wèn)題玛痊。 **