前言
- 前段時間看到一個挺有趣的動畫效果隔心,于是就想著實(shí)現(xiàn)一下,順便練下手尚胞,這里是第一篇教程硬霍,介紹了啟動動畫的制作,示例代碼放在這里笼裳,完整的demo會在最后一篇教程放出
動畫引擎的選擇
- 使用Facebook開源的pop以及iOS自帶的CoreAnimation
實(shí)現(xiàn)指示牌開始階段的動畫
實(shí)現(xiàn)思路是使用一個CALayer作為容器唯卖,也就是containerLayer粱玲,在容器當(dāng)中再放置兩個layer,一個矩形rectLayer與一個三角形triLayer組成一個指示牌拜轨,形變的動畫由容器內(nèi)部的layer來執(zhí)行密幔,而位移的動畫由containerLayer來執(zhí)行
-
具體的代碼如下:
- (void)initRectLayer{ self.rectLayer = [CALayer layer]; self.rectLayer.backgroundColor = [UIColor colorWithWhite:1 alpha:1].CGColor; self.rectLayer.position = CGPointMake(self.containerLayer.frame.size.width/2, self.containerLayer.frame.size.height/4); self.rectLayer.bounds = CGRectMake(0, 0, 24, 30); self.rectLayer.cornerRadius = 2.f; [self.containerLayer addSublayer:self.rectLayer]; } - (void)initTriLayer{ self.triLayer = [CAShapeLayer layer]; [self.containerLayer addSublayer:self.triLayer]; UIBezierPath *bottomBezierPath = [UIBezierPath bezierPath]; self.triLayer.position = CGPointMake(self.containerLayer.frame.size.width/2-20, self.containerLayer.frame.size.height/70); [bottomBezierPath moveToPoint:CGPointMake(0, 28)]; [bottomBezierPath addLineToPoint:CGPointMake(40, 28)]; [bottomBezierPath addLineToPoint:CGPointMake(20, 50)]; [bottomBezierPath closePath]; self.triLayer.path = bottomBezierPath.CGPath; self.triLayer.fillColor = [UIColor whiteColor].CGColor; } - (void)initContainerLayer{ self.containerLayer = [CALayer layer]; self.containerLayer.position = CGPointMake(self.worksheetView.frame.size.width/2, self.worksheetView.frame.size.height/2); self.containerLayer.bounds = CGRectMake(0, 0, 60, 60); self.containerLayer.backgroundColor = [UIColor clearColor ].CGColor; [self.worksheetView.layer addSublayer:self.containerLayer]; } - (void)rectLayerAnimation{ POPBasicAnimation *topScaleAnim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerSize]; topScaleAnim.toValue = [NSValue valueWithCGSize:CGSizeMake(40, 25)]; topScaleAnim.duration = 1; POPBasicAnimation *topPositionAnim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition]; topPositionAnim.toValue = [NSValue valueWithCGPoint:CGPointMake(24, 40)]; topPositionAnim.duration = 0.5; POPBasicAnimation *topCornerAnim = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerCornerRadius]; topCornerAnim.toValue = @3.f; topCornerAnim.duration = 0.5; POPSpringAnimation *shakeAnim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerRotation]; shakeAnim.toValue = @(0.5); shakeAnim.springBounciness = 20; [self.rectLayer pop_addAnimation:topScaleAnim forKey:@"IndiReadyStyleTopScaleAnim"]; [self.rectLayer pop_addAnimation:topCornerAnim forKey:@"IndiReadyStyleTopCornerAnim"]; } - (void)triLayerAnimation{ POPBasicAnimation *bottonScaleAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; bottonScaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(0.25, 0.25)]; bottonScaleAnimation.duration = 0.5; POPBasicAnimation *bottonPosition1stAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition]; bottonPosition1stAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(self.worksheetView.frame.size.width/2-50, self.worksheetView.frame.size.height/2-50)]; bottonPosition1stAnimation.duration = 0.5; POPBasicAnimation *bottonPositionAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerPosition]; bottonPositionAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(self.triLayer.position.x + 15, self.triLayer.position.y+19)]; bottonPositionAnimation.duration = 0.5; [self.triLayer pop_addAnimation:bottonScaleAnimation forKey:@"IndiReadyStyleBottomScaleAnim"]; [self.triLayer pop_addAnimation:bottonPositionAnimation forKey:@"IndiReadyStyleBottomPositionYAnim"]; } - (void)containerLayerAnimation{ CGPoint originPoint = self.containerLayer.position; POPCustomAnimation *customAnimation = [POPCustomAnimation animationWithBlock:^BOOL(id target, POPCustomAnimation *animation) { self.containerLayer.position = CGPointMake(self.containerLayer.position.x - 3, self.containerLayer.position.y - 3); BOOL downSwitch; if (self.containerLayer.position.x - originPoint.x <= -80) { downSwitch = YES; } if (downSwitch) { self.containerLayer.position = CGPointMake(self.containerLayer.position.x, self.containerLayer.position.y+10); } if (self.containerLayer.position.y >= originPoint.y + 41) { return NO; }else{ return YES; } }]; [self.containerLayer pop_addAnimation:customAnimation forKey:@"IndiReadyStyleOutSizeLayerAnim"]; }
實(shí)現(xiàn)開始階段圓變成線的效果
這里實(shí)際上分成兩個階段,一個由實(shí)心圓開始變化撩轰,從實(shí)心圓開始變化成一個圓環(huán)最后消失胯甩;消失的一瞬間替換成貝塞爾曲線,后面就是對貝塞爾曲線進(jìn)行操縱了
-
先說從實(shí)心圓變成圓環(huán)的效果堪嫂,這個只需要借助pop對圓的borderwidth屬性進(jìn)行動畫便可偎箫,非常簡單
- (void)innerCircleLayerAnimation{ POPBasicAnimation *circleLayerBorderWidthAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerBorderWidth]; circleLayerBorderWidthAnimation.toValue = @(2); circleLayerBorderWidthAnimation.duration = 1.f; [self.innerCircleLayer pop_addAnimation:circleLayerBorderWidthAnimation forKey:@"IndiReadyStyleInnerLayerBorderWidthAnim"]; }
-
接下來是圓變成線的效果,消失之后的圓環(huán)會被貝塞爾曲線所取代皆串,實(shí)際上是由四段貝塞爾曲線拼接成的圓淹办,只要畫出貝塞爾曲線幾個關(guān)鍵的變形狀態(tài)便可以使用CoreAnimation來制作過渡的變化效果,最終實(shí)現(xiàn)效果如下:
需要畫出貝塞爾曲線展開過程的兩個關(guān)鍵狀態(tài)恶复,一個是展開到一半的狀態(tài)怜森,另一個是完全展開成一條直線的狀態(tài)
-
展開成一半的貝塞爾曲線效果如圖:
-
展開成一條直線效果如圖:
-
關(guān)鍵的代碼如下:
- (void)initFancyPath{ self.outSizeRectLayer = [CALayer layer]; self.outSizeRectLayer.position = CGPointMake(self.worksheetView.frame.size.width/2,self.worksheetView.frame.size.height/2); self.outSizeRectLayer.bounds = CGRectMake(0, 0, 100, 100); self.outSizeRectLayer.backgroundColor = [UIColor clearColor].CGColor; [self.worksheetView.layer addSublayer:self.outSizeRectLayer]; CGFloat originX = self.containerLayer.bounds.origin.x; CGFloat originY = self.containerLayer.bounds.origin.y; CGFloat originWidth = 100; CGFloat originHeight = 100; self.rectCenter = CGPointMake(originX + originWidth/2, originY + originHeight/2); CGFloat extraX = 0; CGFloat extraY = 0; CGFloat offset = originWidth / 3.6; self.pointA = CGPointMake(self.rectCenter.x + extraX, originY + extraY); self.pointB = CGPointMake(originX + originWidth + extraX, self.rectCenter.y + extraY); self.pointC = CGPointMake(self.rectCenter.x + extraX, originY + originHeight + extraY); self.pointD = CGPointMake(originX + extraX, self.rectCenter.y + extraY); self.pointE = CGPointMake(self.rectCenter.x + extraX, originY + extraY); self.c1 = CGPointMake(self.pointA.x + offset, self.pointA.y); self.c2 = CGPointMake(self.pointB.x, self.pointB.y - offset); self.c3 = CGPointMake(self.pointB.x, self.pointB.y + offset); self.c4 = CGPointMake(self.pointC.x + offset, self.pointC.y); self.c5 = CGPointMake(self.pointC.x - offset, self.pointC.y); self.c6 = CGPointMake(self.pointD.x, self.pointD.y + offset); self.c7 = CGPointMake(self.pointD.x, self.pointD.y - offset); self.c8 = CGPointMake(self.pointA.x - offset, self.pointA.y); self.fancyLine = [UIBezierPath bezierPath]; self.fancyShape = [CAShapeLayer layer]; [self.fancyLine moveToPoint: self.pointA]; [self.fancyLine addCurveToPoint:self.pointB controlPoint1:self.c1 controlPoint2:self.c2]; [self.fancyLine addCurveToPoint:self.pointC controlPoint1:self.c3 controlPoint2:self.c4]; [self.fancyLine addCurveToPoint:self.pointD controlPoint1:self.c5 controlPoint2:self.c6]; [self.fancyLine addCurveToPoint:self.pointE controlPoint1:self.c7 controlPoint2:self.c8]; [self.fancyLine closePath]; self.fancyShape.path = self.fancyLine.CGPath; self.fancyShape.position = CGPointMake(self.rectCenter.x, self.rectCenter.y); self.fancyShape.bounds = CGPathGetBoundingBox(self.fancyShape.path); self.fancyShape.strokeColor = [UIColor blackColor].CGColor; self.fancyShape.fillColor = [UIColor clearColor].CGColor; self.fancyShape.lineWidth = 3.0; [self.outSizeRectLayer addSublayer:self.fancyShape]; } - (void)bezierPathAnimation{ CGPoint pointAm = CGPointMake(self.pointA.x + 90, self.pointA.y); CGPoint pointEm = CGPointMake(self.pointE.x - 90, self.pointE.y); CGPoint pointBm = CGPointMake(self.pointB.x, self.pointB.y); CGPoint pointDm = CGPointMake(self.pointD.x, self.pointD.y); CGPoint c1m = CGPointMake(self.c1.x + 20, self.c1.y + 10); CGPoint c2m = CGPointMake(self.c2.x, self.c2.y + 10); CGPoint c7m = CGPointMake(self.c7.x, self.c7.y + 10); CGPoint c8m = CGPointMake(self.c8.x - 20, self.c8.y + 10); UIBezierPath *crazyPath = [UIBezierPath bezierPath]; [crazyPath moveToPoint:pointAm]; [crazyPath addCurveToPoint:pointBm controlPoint1:c1m controlPoint2:c2m]; [crazyPath addCurveToPoint:self.pointC controlPoint1:self.c3 controlPoint2:self.c4]; [crazyPath addCurveToPoint:pointDm controlPoint1:self.c5 controlPoint2:self.c6]; [crazyPath addCurveToPoint:pointEm controlPoint1:c7m controlPoint2:c8m]; self.fancyShape.path = crazyPath.CGPath; CABasicAnimation *morph = [CABasicAnimation animationWithKeyPath:@"path"]; morph.duration = 0.3; morph.delegate = self; morph.toValue = (id)crazyPath; [self.fancyShape addAnimation:morph forKey:nil]; } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ CGPoint pointAfin = CGPointMake(self.pointA.x + self.worksheetView.frame.size.width/2 - 10, self.pointA.y + 100); CGPoint pointBfin = CGPointMake(self.pointB.x, self.pointB.y + 50); CGPoint pointCfin = CGPointMake(self.pointC.x,self.pointC.y); CGPoint pointDfin = CGPointMake(self.pointD.x, self.pointD.y + 50); CGPoint pointEfin = CGPointMake(self.pointE.x - self.worksheetView.frame.size.width/2 + 10, self.pointE.y + 100); CGPoint c1fin = CGPointMake(self.c1.x + 100, self.c1.y + 100); CGPoint c2fin = CGPointMake(self.c2.x + 80, self.c2.y + 50 + 100/3.6); CGPoint c3fin = CGPointMake(self.c3.x - 5, self.c3.y + 50 - 100/3.6); CGPoint c4fin = CGPointMake(self.c4.x, self.c4.y); CGPoint c5fin = CGPointMake(self.c5.x, self.c5.y); CGPoint c6fin = CGPointMake(self.c6.x + 5, self.c6.y + 50 - 100/3.6); CGPoint c7fin = CGPointMake(self.c7.x - 80, self.c7.y + 50 + 100/3.6); CGPoint c8fin = CGPointMake(self.c8.x - 100, self.c8.y + 100); UIBezierPath *lineFinalPath = [UIBezierPath bezierPath]; [lineFinalPath moveToPoint:pointAfin]; [lineFinalPath addCurveToPoint:pointBfin controlPoint1:c1fin controlPoint2:c2fin]; [lineFinalPath addCurveToPoint:pointCfin controlPoint1:c3fin controlPoint2:c4fin]; [lineFinalPath addCurveToPoint:pointDfin controlPoint1:c5fin controlPoint2:c6fin]; [lineFinalPath addCurveToPoint:pointEfin controlPoint1:c7fin controlPoint2:c8fin]; self.fancyShape.path = lineFinalPath.CGPath; CABasicAnimation *morph = [CABasicAnimation animationWithKeyPath:@"path"]; morph.duration = 0.3; morph.toValue = (id)lineFinalPath; [self.fancyShape addAnimation:morph forKey:nil]; }
曲線的坐標(biāo)計(jì)算借助了一個外接矩形進(jìn)行輔助定位
ok,第一篇教程就到此結(jié)束了谤牡,第二篇將介紹下載過程指示牌移動的動畫