干貨系列之手把手教你使用Core animation 做動畫

源碼下載:源碼

最近在技術群里,有人發(fā)了一張帶有動畫效果的圖片肌蜻。覺得很有意思,便動手實現(xiàn)了一下必尼。在這篇文章中你將會學到Core Animation顯式動畫中的關鍵幀動畫蒋搜、組合動畫、CABasicAnimation動畫判莉。先上一張原圖的動畫效果豆挽。

點擊此查看[原圖動畫效果](https://github.com/WZF-Fei/ZFChangeAnimation/blob/master/origin animation.gif)。

本文要實現(xiàn)的效果圖如下:


實現(xiàn)的效果圖.gif

把原動畫gif動畫在mac上使用圖片瀏覽模式打開券盅,我們可以看到動畫每一幀的顯示帮哈。從每一幀上的展示過程,可以把整體的動畫進行拆分成兩大部分锰镀。

第一部分(Part1)從初始狀態(tài)變成取消狀態(tài)(圖片上是由橫實線變成上線橫線交叉的圓)娘侍。
第二部分(Part2)從取消狀態(tài)變回初始狀態(tài)。

下面我們先詳細分析Part1是怎么實現(xiàn)的泳炉。根據(jù)動畫圖憾筏,把Part1再細分成三步。

Step1 : 中間橫實線的由右向左的運動效果花鹅。這其實是一個組合動畫氧腰。是先向左偏移的同時橫線變短。先看一下實現(xiàn)的動態(tài)效果。

step1 Animation.gif

■ 向左偏移---使用基本動畫中animationWithKeyPath鍵值對的方式來改變動畫的值古拴。我們這里使用position.x,同樣可以使用transform.translation.x來平移箩帚。

■ 改變橫線的大小---使用經(jīng)典的strokeStartstrokeEnd。其實上橫線長度的變化的由strokeStartstrokeEnd之間的值來共同來決定黄痪。改變strokeEnd的值由1.0到0.4紧帕,不改變strokeStart的值。橫線的長度會從右側(cè)方向由1.0倍長度減少到0.4倍長度桅打。參見示意圖的紅色區(qū)域是嗜。

stroke示意圖.png
-(void) animationStep1{
    
    //最終changedLayer的狀態(tài)
    _changedLayer.strokeEnd = 0.4;
    //基本動畫,長度有1.0減少到0.4
    CABasicAnimation *strokeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    strokeAnimation.fromValue = [NSNumber numberWithFloat:1.0f];
    strokeAnimation.toValue = [NSNumber numberWithFloat:0.4f];
    //基本動畫油额,向左偏移10個像素
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"position.x"];
    pathAnimation.fromValue = [NSNumber numberWithFloat:0.0];
    pathAnimation.toValue = [NSNumber numberWithFloat:-10];
    //組合動畫,平移和長度減少同時進行
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.animations = [NSArray arrayWithObjects:strokeAnimation,pathAnimation, nil];
    animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    animationGroup.duration = kStep1Duration;
    //設置代理
    animationGroup.delegate = self;
    animationGroup.removedOnCompletion = YES;
    //監(jiān)聽動畫
    [animationGroup setValue:@"animationStep1" forKey:@"animationName"];
    //動畫加入到changedLayer上
    [_changedLayer addAnimation:animationGroup forKey:nil];
}

Step2 : 由左向右的動畫--向右偏移同時橫線長度變長刻帚×仕唬看一下Step2要實現(xiàn)的動畫效果。其思路和Step1是一樣的崇众。

step2 Animation.gif
-(void)animationStep2
{
    CABasicAnimation *translationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
    translationAnimation.fromValue = [NSNumber numberWithFloat:-10];
    //strokeEnd:0.8 剩余的距離toValue = lineWidth * (1 - 0.8);

    translationAnimation.toValue = [NSNumber numberWithFloat:0.2 * lineWidth ];
    
    _changedLayer.strokeEnd = 0.8;
    CABasicAnimation *strokeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    strokeAnimation.fromValue = [NSNumber numberWithFloat:0.4f];
    strokeAnimation.toValue = [NSNumber numberWithFloat:0.8f];
    
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.animations = [NSArray arrayWithObjects:strokeAnimation,translationAnimation, nil];
    animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    animationGroup.duration = kStep2Duration;
    //設置代理
    animationGroup.delegate = self;
    animationGroup.removedOnCompletion = YES;
    [animationGroup setValue:@"animationStep2" forKey:@"animationName"];
    [_changedLayer addAnimation:animationGroup forKey:nil];
}

Step3: 圓弧的動畫效果和上下兩個橫實線的動畫效果掂僵。

  1. 畫圓弧,首先想到是使用UIBezierPath顷歌。畫個示意圖來分析動畫路徑锰蓬。示意圖如下:
step3 示意圖.jpg

整個path路徑是由三部分組成,ABC曲線眯漩、CD圓弧芹扭、DD′圓
使用UIBezierPath的方法

- (void)appendPath:(UIBezierPath *)bezierPath;

把三部分路徑關聯(lián)起來赦抖。詳細講解思路舱卡。

? ABC曲線就是貝塞爾曲線,可以根據(jù)A队萤、B轮锥、C三點的位置使用方法

//endPoint 終點坐標 controlPoint1 起點坐標
//controlPoint2 起點和終點在曲線上的切點延伸相交的交點坐標
- (void)addCurveToPoint:(CGPoint)endPoint 
          controlPoint1:(CGPoint)controlPoint1 
          controlPoint2:(CGPoint)controlPoint2;

二次貝塞爾曲線示意圖如下:

二次貝塞爾曲線.png

其中control point 點是從曲線上取 start point和end point 切點相交匯的所得到的交點。如下圖:

control point .png

首先C點取圓上的一點要尔,-30°舍杜。那么,

CGFloat angle = Radians(30);

C點坐標為:

    //C點
    CGFloat endPointX = self.center.x + Raduis * cos(angle);
    CGFloat endPointY = kCenterY - Raduis * sin(angle);

A點坐標為:

    //A點 取橫線最右邊的點
    CGFloat startPointX = self.center.x + lineWidth/2.0 ;
    CGFloat startPointY = controlPointY;

control point 為E點:

    //E點 半徑*反余弦(30°)
    CGFloat startPointX = self.center.x + Raduis *acos(angle);
    CGFloat startPointY = controlPointY;

? CD圓弧的路徑使用此方法確定

+ (instancetype)bezierPathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

關于弧度問題赵辕,UIBezierPath的官方文檔中的這張圖:

弧度.jpg

StartAngle 弧度即C點弧度既绩,EndAngel弧度即D點弧度。

CGFloat StartAngle = 2 * M_PI - angle;
CGFloat EndAngle = M_PI + angle;

? DD′圓的路徑和上面2一樣的方法確定还惠。

StartAngle 弧度即D點弧度熬词,EndAngel弧度即D′點弧度。

CGFloat StartAngle = M_PI *3/2 - (M_PI_2 -angle);
CGFloat EndAngle = -M_PI_2 - (M_PI_2 -angle);

下面部分代碼是所有path路徑。

    UIBezierPath *path = [UIBezierPath bezierPath];
    
    // 畫貝塞爾曲線 圓弧
    [path moveToPoint:CGPointMake(self.center.x +  lineWidth/2.0 , kCenterY)];
    
     CGFloat angle = Radians(30);
    //C點
    CGFloat endPointX = self.center.x + Raduis * cos(angle);
    CGFloat endPointY = kCenterY - Raduis * sin(angle);
    //A點
    CGFloat startPointX = self.center.x + lineWidth/2.0;
    CGFloat startPointY = kCenterY;
    //E點 半徑*反余弦(30°)
    CGFloat controlPointX = self.center.x + Raduis *acos(angle);
    CGFloat controlPointY = kCenterY;

    //貝塞爾曲線 ABC曲線
    [path addCurveToPoint:CGPointMake(endPointX, endPointY)
            controlPoint1:CGPointMake(startPointX , startPointY)
            controlPoint2:CGPointMake(controlPointX , controlPointY)];
    
    // (360°- 30°) ->(180°+30°) 逆時針的圓弧 CD圓弧
    UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.center.x,kCenterY)
                                                         radius:Raduis
                                                     startAngle:2 * M_PI - angle
                                                       endAngle:M_PI + angle
                                                      clockwise:NO];
    [path appendPath:path1];
     // (3/2π- 60°) ->(-1/2π -60°) 逆時針的圓 DD′圓
    UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.center.x,kCenterY)
                                                        radius:Raduis
                                                    startAngle:M_PI *3/2 - (M_PI_2 -angle)
                                                      endAngle:-M_PI_2 - (M_PI_2 -angle)
                                                     clockwise:NO];
    

    [path appendPath:path2];

    _changedLayer.path = path.CGPath;

Path路徑有了互拾,接著實現(xiàn)動畫效果歪今。
圓弧的長度逐漸變長。我們還是使用經(jīng)典的strokeStartstrokeEnd颜矿。但是圓弧是如何變長的呢寄猩?

(1) 初始圓弧有一段長度。
(2) 在原始長度的基礎上逐漸變長骑疆,逐漸遠離A點田篇,同時要在D點停止。
(3) 長度逐漸變長箍铭,最終要在D與D′點交匯泊柬。

我們分別解決這個三個問題。

第一個問題诈火,strokeEnd - strokeStart > 0這樣能保證有一段圓弧兽赁。

第二個問題,逐漸變長,意味著strokeEnd值不斷變大冷守。遠離A點意味著strokeStart的值不斷變大刀崖。在D點停止,說明了strokeStart有上限值拍摇。

第三個問題亮钦,意味著strokeEnd值不斷變大,最終值為1.0充活。

這三個問題說明了一個問題蜂莉,strokeEndstrokeStart是一組變化的數(shù)據(jù)。

那么core animation 中可以控制一組值的動畫是關鍵幀動畫(CAKeyframeAnimation)混卵。

為了更準確的給出strokeEndstrokeStart值巡语,我們使用長度比來確定。

假設我們初始的長度就是曲線ABC的長度淮菠。但是貝塞爾曲線長度怎么計算男公?使用下面方法:

//求貝塞爾曲線長度
-(CGFloat) bezierCurveLengthFromStartPoint:(CGPoint)start toEndPoint:(CGPoint) end withControlPoint:(CGPoint) control
{
    const int kSubdivisions = 50;
    const float step = 1.0f/(float)kSubdivisions;
    
    float totalLength = 0.0f;
    CGPoint prevPoint = start;
    
    // starting from i = 1, since for i = 0 calulated point is equal to start point
    for (int i = 1; i <= kSubdivisions; i++)
    {
        float t = i*step;
        
        float x = (1.0 - t)*(1.0 - t)*start.x + 2.0*(1.0 - t)*t*control.x + t*t*end.x;
        float y = (1.0 - t)*(1.0 - t)*start.y + 2.0*(1.0 - t)*t*control.y + t*t*end.y;
        
        CGPoint diff = CGPointMake(x - prevPoint.x, y - prevPoint.y);
        
        totalLength += sqrtf(diff.x*diff.x + diff.y*diff.y); // Pythagorean
        
        prevPoint = CGPointMake(x, y);
    }
    
    return totalLength;
}

計算貝塞爾曲線所在的比例為:

CGFloat orignPercent = [self calculateCurveLength]/[self calculateTotalLength];

初始的strokeStart = 0strokeEnd = orignPercent合陵。
最終的stokeStart = 枢赔?

//結(jié)果就是貝塞爾曲線長度加上120°圓弧的長度與總長度相比得到的結(jié)果。
CGFloat endPercent =([self calculateCurveLength] + Radians(120) *Raduis ) / [self calculateTotalLength];

實現(xiàn)動畫的代碼為

    CGFloat orignPercent = [self calculateCurveLength] / [self calculateTotalLength];
    CGFloat endPercent =([self calculateCurveLength] + Radians(120) *Raduis ) / [self calculateTotalLength];
    
    _changedLayer.strokeStart = endPercent;
    
    //方案1
    CAKeyframeAnimation *startAnimation = [CAKeyframeAnimation animationWithKeyPath:@"strokeStart"];
    startAnimation.values = @[@0.0,@(endPercent)];
    
    CAKeyframeAnimation *EndAnimation = [CAKeyframeAnimation animationWithKeyPath:@"strokeEnd"];
    EndAnimation.values = @[@(orignPercent),@1.0];
    
    
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.animations = [NSArray arrayWithObjects:startAnimation,EndAnimation, nil];
    animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    animationGroup.duration = kStep3Duration;
    animationGroup.delegate = self;
    animationGroup.removedOnCompletion = YES;
    [animationGroup setValue:@"animationStep3" forKey:@"animationName"];
    [_changedLayer addAnimation:animationGroup forKey:nil];

效果圖為:

step3-1 Animation.gif

2.上下橫線的動畫效果拥知。

此動畫效果踏拜,需要使用transform.rotation.z轉(zhuǎn)動角度。

上橫線轉(zhuǎn)動的角度順序為 0 -> 10° -> (-55°) -> (-45°)
這是一組數(shù)據(jù)低剔,使用關鍵幀處理動畫速梗。

    CAKeyframeAnimation *rotationAnimation1 = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation1.values = @[[NSNumber numberWithFloat:0],
                                 [NSNumber numberWithFloat:Radians(10) ],
                                 [NSNumber numberWithFloat:Radians(-10) - M_PI_4 ],
                                 [NSNumber numberWithFloat:- M_PI_4 ]
                                 ];

下橫線轉(zhuǎn)動的角度順序為0 -> (-10°) -> (55°) -> (45°)

    CAKeyframeAnimation *rotationAnimation2 = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation2.values = @[[NSNumber numberWithFloat:0],
                                 [NSNumber numberWithFloat:Radians(-10) ],
                                 [NSNumber numberWithFloat:Radians(10)  + M_PI_4 ],
                                 [NSNumber numberWithFloat: M_PI_4 ]
                                 ];

你認為這么就結(jié)束了? 最終結(jié)束的動畫如下:


step3-2 finished Animation.jpg

發(fā)現(xiàn)相交的直線沒有居中肮塞,而是靠左顯示。

向左平移姻锁,使用transform.translation.x

    //平移量
    CGFloat toValue = lineWidth *(1- cos(M_PI_4)) /2.0;

即旋轉(zhuǎn)角度又發(fā)生偏移量枕赵,使用組合動畫。

上橫線組合動畫

     //平移x
    CABasicAnimation *translationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
    translationAnimation.fromValue = [NSNumber numberWithFloat:0];
    translationAnimation.toValue = [NSNumber numberWithFloat:-toValue];
    
    //角度關鍵幀 上橫線的關鍵幀 0 - 10° - (-55°) - (-45°)
    CAKeyframeAnimation *rotationAnimation1 = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation1.values = @[[NSNumber numberWithFloat:0],
                                 [NSNumber numberWithFloat:Radians(10) ],
                                 [NSNumber numberWithFloat:Radians(-10) - M_PI_4 ],
                                 [NSNumber numberWithFloat:- M_PI_4 ]
                                 ];
  
    
    CAAnimationGroup *transformGroup1 = [CAAnimationGroup animation];
    transformGroup1.animations = [NSArray arrayWithObjects:rotationAnimation1,translationAnimation, nil];
    transformGroup1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    transformGroup1.duration = kStep3Duration;
    transformGroup1.removedOnCompletion = YES;
    [_topLineLayer addAnimation:transformGroup1 forKey:nil];

下橫線組合動畫

    //角度關鍵幀 下橫線的關鍵幀 0 - (-10°) - (55°) - (45°)
    CAKeyframeAnimation *rotationAnimation2 = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation2.values = @[[NSNumber numberWithFloat:0],
                                 [NSNumber numberWithFloat:Radians(-10) ],
                                 [NSNumber numberWithFloat:Radians(10) + M_PI_4 ],
                                 [NSNumber numberWithFloat: M_PI_4 ]
                                 ];
    

    CAAnimationGroup *transformGroup2 = [CAAnimationGroup animation];
    transformGroup2.animations = [NSArray arrayWithObjects:rotationAnimation2,translationAnimation, nil];
    transformGroup2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    transformGroup2.duration = kStep3Duration ;
    transformGroup2.delegate = self;
    transformGroup2.removedOnCompletion = YES;
    [_bottomLineLayer addAnimation:transformGroup2 forKey:nil];

Part1到此結(jié)束位隶。最終效果圖


Part1 animation.gif

Part2的思路和Part1思路是一樣的拷窜。你可以參考代碼自己思考一下。核心代碼

-(void)cancelAnimation
{
    //最關鍵是path路徑
   
    UIBezierPath *path = [UIBezierPath bezierPath];
    //30度,經(jīng)過反復測試涧黄,效果最好
    CGFloat angle = Radians(30);
    
    CGFloat startPointX = self.center.x + Raduis * cos(angle);
    CGFloat startPointY = kCenterY - Raduis * sin(angle);
    
    CGFloat controlPointX = self.center.x + Raduis *acos(angle);
    CGFloat controlPointY = kCenterY;
    
    CGFloat endPointX = self.center.x + lineWidth /2;
    CGFloat endPointY = kCenterY;
    
    //組合path 路徑 起點 -150° 順時針的圓
    path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.center.x,kCenterY)
                                                         radius:Raduis
                                                     startAngle:-M_PI + angle
                                                       endAngle:M_PI + angle
                                                      clockwise:YES];
    
    
    
    //起點為 180°-> (360°-30°)
    UIBezierPath *path1 = [UIBezierPath bezierPathWithArcCenter:CGPointMake(self.center.x,kCenterY)
                                                         radius:Raduis
                                                     startAngle:M_PI + angle
                                                       endAngle:2 * M_PI - angle
                                                      clockwise:YES];
    [path appendPath:path1];

    //三點曲線
    UIBezierPath *path2 = [UIBezierPath bezierPath];

    [path2 moveToPoint:CGPointMake(startPointX, startPointY)];
    
    [path2 addCurveToPoint:CGPointMake(endPointX,endPointY)
             controlPoint1:CGPointMake(startPointX, startPointY)
             controlPoint2:CGPointMake(controlPointX, controlPointY)];
    
    [path appendPath:path2];
    
    //比原始狀態(tài)向左偏移5個像素
    UIBezierPath *path3 = [UIBezierPath bezierPath];
    [path3 moveToPoint:CGPointMake(endPointX,endPointY)];
    [path3 addLineToPoint:CGPointMake(self.center.x - lineWidth/2 -5,endPointY)];
    [path appendPath:path3];
    
    _changedLayer.path = path.CGPath;
    
    //平移量
    CGFloat toValue = lineWidth *(1- cos(M_PI_4)) /2.0;
    //finished 最終狀態(tài)
    CGAffineTransform transform1 = CGAffineTransformMakeRotation(0);
    CGAffineTransform transform2 = CGAffineTransformMakeTranslation(0, 0);
    CGAffineTransform transform3 = CGAffineTransformMakeRotation(0);
    
    CGAffineTransform transform = CGAffineTransformConcat(transform1, transform2);
    _topLineLayer.affineTransform = transform;
    transform = CGAffineTransformConcat(transform3, transform2);
    _bottomLineLayer.affineTransform = transform;
    
    //一個圓的長度比
    CGFloat endPercent = 2* M_PI *Raduis / ([self calculateTotalLength] + lineWidth);

    
    //橫線占總path的百分比
    CGFloat percent = lineWidth / ([self calculateTotalLength] + lineWidth);
    
    _changedLayer.strokeStart = 1.0 -percent;
    
    CAKeyframeAnimation *startAnimation = [CAKeyframeAnimation animationWithKeyPath:@"strokeStart"];
    startAnimation.values = @[@0.0,@0.3,@(1.0 -percent)];
    
    //在π+ angle
    CAKeyframeAnimation *EndAnimation = [CAKeyframeAnimation animationWithKeyPath:@"strokeEnd"];
    EndAnimation.values = @[@(endPercent),@(endPercent),@1.0];
    
    
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.animations = [NSArray arrayWithObjects:startAnimation,EndAnimation, nil];
    animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    animationGroup.duration = kStep4Duration;
    animationGroup.delegate = self;
    animationGroup.removedOnCompletion = YES;
    [animationGroup setValue:@"animationStep4" forKey:@"animationName"];
    [_changedLayer addAnimation:animationGroup forKey:nil];
    
    //平移x
    CABasicAnimation *translationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
    translationAnimation.fromValue = [NSNumber numberWithFloat:-toValue];
    translationAnimation.toValue = [NSNumber numberWithFloat:0];
    
    //角度關鍵幀 上橫線的關鍵幀  (-45°) -> (-55°)-> 10° -> 0
    CAKeyframeAnimation *rotationAnimation1 = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation1.values = @[[NSNumber numberWithFloat:- M_PI_4 ],
                                  [NSNumber numberWithFloat:- Radians(10) - M_PI_4 ],
                                  [NSNumber numberWithFloat:Radians(10) ],
                                  [NSNumber numberWithFloat:0]
                                  ];
    

    CAAnimationGroup *transformGroup1 = [CAAnimationGroup animation];
    transformGroup1.animations = [NSArray arrayWithObjects:rotationAnimation1,translationAnimation, nil];
    transformGroup1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    transformGroup1.duration = kStep4Duration;
    transformGroup1.removedOnCompletion = YES;
    [_topLineLayer addAnimation:transformGroup1 forKey:nil];
    
    //角度關鍵幀 下橫線的關鍵幀  (45°)-> (55°)- >(-10°)-> 0
    CAKeyframeAnimation *rotationAnimation2 = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation2.values = @[[NSNumber numberWithFloat: M_PI_4 ],
                                  [NSNumber numberWithFloat:Radians(10) + M_PI_4 ],
                                  [NSNumber numberWithFloat:-Radians(10) ],
                                  [NSNumber numberWithFloat:0]
                                  ];

    CAAnimationGroup *transformGroup2 = [CAAnimationGroup animation];
    transformGroup2.animations = [NSArray arrayWithObjects:rotationAnimation2,translationAnimation, nil];
    transformGroup2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    transformGroup2.duration = kStep4Duration;
    transformGroup2.delegate = self;
    transformGroup2.removedOnCompletion = YES;
    [_bottomLineLayer addAnimation:transformGroup2 forKey:nil];
    
}

最終效果圖:


finished animation.gif

本篇文章講解結(jié)束篮昧!

代碼點此鏈接下載:https://github.com/WZF-Fei/ZFChangeAnimation

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市笋妥,隨后出現(xiàn)的幾起案子懊昨,更是在濱河造成了極大的恐慌,老刑警劉巖春宣,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酵颁,死亡現(xiàn)場離奇詭異,居然都是意外死亡信认,警方通過查閱死者的電腦和手機材义,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門均抽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫁赏,“玉大人,你說我怎么就攤上這事油挥×视” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵深寥,是天一觀的道長攘乒。 經(jīng)常有香客問我,道長惋鹅,這世上最難降的妖魔是什么则酝? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮闰集,結(jié)果婚禮上沽讹,老公的妹妹穿的比我還像新娘。我一直安慰自己武鲁,他們只是感情好爽雄,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沐鼠,像睡著了一般挚瘟。 火紅的嫁衣襯著肌膚如雪叹谁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天乘盖,我揣著相機與錄音焰檩,去河邊找鬼。 笑死侧漓,一個胖子當著我的面吹牛锅尘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播布蔗,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼藤违,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纵揍?” 一聲冷哼從身側(cè)響起顿乒,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泽谨,沒想到半個月后璧榄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡吧雹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年骨杂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雄卷。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡搓蚪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出丁鹉,到底是詐尸還是另有隱情妒潭,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布揣钦,位于F島的核電站雳灾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏冯凹。R本人自食惡果不足惜谎亩,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宇姚。 院中可真熱鬧匈庭,春花似錦、人聲如沸空凸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呀洲。三九已至紊选,卻和暖如春啼止,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兵罢。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工献烦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卖词。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓巩那,卻偏偏與公主長得像,于是被迫代替她去往敵國和親此蜈。 傳聞我的和親對象是個殘疾皇子即横,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容