核心動(dòng)畫可以在layer上創(chuàng)造復(fù)雜的動(dòng)畫,比如修改大小、位置、旋轉(zhuǎn)變換等等,也可以同時(shí)修改一個(gè)或多個(gè)屬性。
簡(jiǎn)單修改layer屬性做動(dòng)畫
首先理解下隱式動(dòng)畫和顯示動(dòng)畫。當(dāng)修改圖層任意屬性時(shí)都會(huì)出發(fā)隱式動(dòng)畫,隱式動(dòng)畫有默認(rèn)時(shí)間步調(diào)和其他屬性。如果不使用系統(tǒng)的默認(rèn)動(dòng)畫可以通過(guò)創(chuàng)建CABasicAnimation對(duì)象指定圖層的顯示動(dòng)畫。調(diào)用addAnimation:forKey:方法把動(dòng)畫添加到圖層上
在之前的矢量圖上添加一個(gè)縮放的動(dòng)畫
CAShapeLayer *layer1 = [CAShapeLayer layer];
layer1.bounds = CGRectMake(0, 0, 200, 200);
layer1.position = self.view.center;
layer1.backgroundColor = [UIColor colorWithRed:0.9 green:0.3 blue:0.3 alpha:0.3].CGColor;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(50, 0)];
[path addCurveToPoint:CGPointMake(0, 50)
controlPoint1:CGPointMake(50, 25)
controlPoint2:CGPointMake(25, 50)];
[path addCurveToPoint:CGPointMake(-50, 0)
controlPoint1:CGPointMake(-25, 50)
controlPoint2:CGPointMake(-50, 25)];
[path addCurveToPoint:CGPointMake(0, -50)
controlPoint1:CGPointMake(-50, -25)
controlPoint2:CGPointMake(-25, -50)];
[path addCurveToPoint:CGPointMake(50, 0)
controlPoint1:CGPointMake(25, -50)
controlPoint2:CGPointMake(50, -25)];
CGAffineTransform transform = CGAffineTransformMakeTranslation(100, 100);
[path applyTransform:transform];
layer1.path = path.CGPath;
layer1.fillColor = [UIColor whiteColor].CGColor;
layer1.strokeColor = [UIColor blueColor].CGColor;
[self.view.layer addSublayer:layer1];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.5, 1.5, 1)];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.2, 0.2, 1)];
animation.duration = 4.0;
[layer1 addAnimation:animation forKey:@"animation1"];
<img src="http://upload-images.jianshu.io/upload_images/1840221-e8152fe11bd3915a.gif?imageMogr2/auto-orient/strip" width=250>
要注意,顯式動(dòng)畫只是提供一個(gè)動(dòng)畫,它并不會(huì)修改圖層對(duì)象的數(shù)據(jù)。所以在動(dòng)畫結(jié)束時(shí),需要更新圖層對(duì)象的數(shù)據(jù)。
動(dòng)畫會(huì)在runloop結(jié)束時(shí)開(kāi)始執(zhí)行,因此當(dāng)前線程必須要有一個(gè)runloop才可以讓動(dòng)畫執(zhí)行。即便改變圖層的多個(gè)屬性或是添加多個(gè)動(dòng)畫,他們都會(huì)在同一時(shí)刻執(zhí)行。比如你可以在移動(dòng)的時(shí)候同時(shí)修改圖層的不透明度。雖然他們是同時(shí)執(zhí)行的,但是你可以單獨(dú)配置每個(gè)屬性的動(dòng)畫對(duì)象,控制他們屬性變化的時(shí)間節(jié)奏。
用關(guān)鍵幀動(dòng)畫修改layer屬性
關(guān)鍵幀動(dòng)畫可以在一個(gè)動(dòng)畫周期內(nèi)給屬性設(shè)置多個(gè)值。關(guān)鍵幀動(dòng)畫由一系列的值和每?jī)蓚€(gè)值之間變化時(shí)的時(shí)間節(jié)奏組成,他們都用數(shù)組表示。
CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
keyframeAnimation.keyTimes = @[@(0),@(0.3),@(0.5),@(1)];
keyframeAnimation.values = @[[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.5, 1.5, 1)],
[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.2, 1.2, 1)],
[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 0.2, 1)],
[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.3, 1.2, 1)]];
keyframeAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
keyframeAnimation.duration = 8.f;
keyframeAnimation.fillMode = kCAFillModeForwards;
keyframeAnimation.removedOnCompletion = NO;
[layer1 addAnimation:keyframeAnimation forKey:@"transform2"];
<img src="http://upload-images.jianshu.io/upload_images/1840221-e58e4631a46e29fd.gif?imageMogr2/auto-orient/strip" width=250>
動(dòng)畫屬性keyPath
在第一行創(chuàng)建關(guān)鍵幀動(dòng)畫時(shí)調(diào)用方法animationWithKeyPath,并傳入動(dòng)畫屬性keypath。CAPropertyAnimation定義了動(dòng)畫的屬性值,它支持很多屬性,具體可以查閱官方文檔。
關(guān)鍵幀的值
通過(guò)動(dòng)畫的path或values來(lái)設(shè)置關(guān)鍵幀的值。屬性是CGPoint類型的(錨點(diǎn)或位置)通常用動(dòng)畫的path屬性來(lái)設(shè)置,path是CGPathRef類型。也可以用values屬性來(lái)設(shè)置,它是數(shù)組類型。這時(shí)需要把CGPoint類型用NSValue來(lái)進(jìn)行封裝再添加到數(shù)組中,同樣的CATransform3D和CGRect類型用NSValue封裝、 CGFloat類型用NSNumber封裝。
關(guān)鍵幀之間的節(jié)奏
關(guān)鍵幀動(dòng)畫的時(shí)間節(jié)奏和插值是通過(guò)一系列屬性共同控制的:
- calculationMode:動(dòng)畫中的計(jì)算算法,這個(gè)屬性同時(shí)影響其他屬性的使用
- kCAAnimationLinear 或 kCAAnimationCubic,關(guān)鍵幀之間使用直線或圓滑曲線的插值算法實(shí)現(xiàn)。 當(dāng)設(shè)置為kCAAnimationCubic時(shí),可通過(guò)參數(shù)調(diào)整差值曲線函數(shù)來(lái)完全控制中間幀變化方法。
- kCAAnimationPaced or kCAAnimationCubicPaced,使keyTimes和timingFunctions屬性失效。動(dòng)畫平滑過(guò)渡,也就是勻速進(jìn)行。
- kCAAnimationDiscrete,使timingFunctions屬性失效。動(dòng)畫在關(guān)鍵幀之間跳躍進(jìn)行,也就是中間幀不進(jìn)行差值計(jì)算,直接從兩個(gè)關(guān)鍵幀變化。
- keyTimes 指定每一個(gè)關(guān)鍵幀的屬性值
- timingFunctions 指定每?jī)蓚€(gè)關(guān)鍵幀之間過(guò)度的時(shí)間曲線變化
動(dòng)畫協(xié)議
你會(huì)發(fā)現(xiàn)第一個(gè)demo動(dòng)畫結(jié)束時(shí),layer回到了初始狀態(tài),而第二個(gè)demo在開(kāi)始和結(jié)束時(shí)layer保持動(dòng)畫開(kāi)始和結(jié)束的狀態(tài)。因?yàn)槲以O(shè)置了fillMode和removedOnCompletion這兩個(gè)屬性。layer的transform值始終都是CATransform3DIdentity,但是設(shè)置了這兩個(gè)屬性后可以讓圖層動(dòng)畫開(kāi)始時(shí)就渲染到CATransform3DMakeScale(1.5, 1.5, 1),結(jié)束時(shí)渲染到CATransform3DMakeScale(0.3, 1.2, 1)。
CAMediaTiming協(xié)議是一套抽象接口,描述了動(dòng)畫的一些信息,CALayer和CAAnimation都實(shí)現(xiàn)了該協(xié)議
- beginTime 相對(duì)于父圖層或動(dòng)畫的開(kāi)始時(shí)間
- duration 動(dòng)畫的單次時(shí)長(zhǎng)
- speed 動(dòng)畫的播放速率,默認(rèn)是1
- timeOffset 動(dòng)畫的時(shí)間偏移,就是從哪里開(kāi)始播放,默認(rèn)是0
- repeatCount 動(dòng)畫的重復(fù)播放次數(shù),默認(rèn)是0
- repeatDuration 動(dòng)畫的有效時(shí)長(zhǎng),默認(rèn)是0。結(jié)合重復(fù)次數(shù)使用
- autoreverses 如果是true,動(dòng)畫正播后再進(jìn)行反播。默認(rèn)是false
- fillMode 動(dòng)畫的填充模式,這就是上面用到的。可以四種情況:渲染開(kāi)始和結(jié)束維持layer的屬性值、在動(dòng)畫結(jié)束時(shí)layer渲染到動(dòng)畫結(jié)束時(shí)的屬性、在動(dòng)畫開(kāi)始時(shí)layer渲染到動(dòng)畫開(kāi)始時(shí)的屬性、開(kāi)始結(jié)束都渲染。
removedOnCompletion是CAAnimation的屬性,它表示動(dòng)畫一旦結(jié)束渲染就停止,所以第一個(gè)demo看到動(dòng)畫結(jié)束時(shí)layer又變回了初始的樣子。
運(yùn)行時(shí)停止動(dòng)畫
動(dòng)畫通常會(huì)一直運(yùn)行到結(jié)束,如果想提前中止有兩種方法:
- 移除一個(gè)動(dòng)畫調(diào)用圖層的removeAnimationForKey:方法,參數(shù)和添加動(dòng)畫時(shí)addAnimation:forKey:的第二個(gè)參數(shù)相同,都不能為nil
- 移除全部動(dòng)畫調(diào)用圖層的removeAllAnimations方法,它會(huì)馬上中止所有正在進(jìn)行的動(dòng)畫,并對(duì)layer進(jìn)行重繪。但這個(gè)方法最好不要用!
當(dāng)從圖層移除一個(gè)動(dòng)畫的時(shí)候,會(huì)出發(fā)圖層的重新繪制。圖層會(huì)跳躍到動(dòng)畫之前狀態(tài),如果想保持最后一幀的狀態(tài)可以通過(guò)修改圖層屬性實(shí)現(xiàn)。
動(dòng)畫組合
如果想圖層一直執(zhí)行多個(gè)動(dòng)畫,可以創(chuàng)建CAAnimationGroup對(duì)象通過(guò)配置animations屬性來(lái)實(shí)現(xiàn)。動(dòng)畫組的的事件節(jié)奏和時(shí)長(zhǎng)屬性會(huì)覆蓋掉所包含的動(dòng)畫屬性。
檢測(cè)動(dòng)畫結(jié)束
當(dāng)動(dòng)畫開(kāi)始和結(jié)束時(shí),可以通過(guò)回調(diào)得知:
- 對(duì)動(dòng)畫事物CATransaction添加setCompletionBlock:方法,在動(dòng)畫結(jié)束時(shí)調(diào)用
- 動(dòng)畫對(duì)象實(shí)現(xiàn)animationDidStart:和animationDidStop:finished:方法
如果你想連接兩個(gè)動(dòng)畫盡量不要用這種方式,通過(guò)設(shè)置動(dòng)畫的beginTime來(lái)實(shí)現(xiàn)。