愛奇藝播放按鈕動畫解析

一雳刺、動畫過程分析

1劫灶、拆分動畫

正常動畫效果如下:

正常動畫效果

操作 現(xiàn)象
放慢動畫
這個效果可以反復(fù)多看幾次,觀察一下這個動畫是由那幾部分組成的掖桦,然后再往下看
添加輔助顏色
可以看出本昏,整個動畫有四部分組成:1、左側(cè)豎線 2枪汪、右側(cè)豎線 3涌穆、三角形 4、底部過渡圓弧
只保留豎線
豎線的動畫包括兩部分雀久,一部分是開始的“預(yù)備”動畫和結(jié)束時的“慣性”動畫宿稀;第二步分是在動畫過程中和圓弧、三角的“銜接”動畫
只保留三角動畫
三角動畫正向過程是從左側(cè)豎線頭部赖捌,順時針繪制一個三角形祝沸;逆向過程就是這個動畫的逆向
只保留弧線
弧線動畫主要的作用就是在正向和逆向過程中橋接左右豎線

2、總結(jié)

動畫是由兩部分組成:
1越庇、豎線在開始時的預(yù)備動畫和結(jié)束時的慣性動畫
2罩锐、豎線、三角形和弧線的銜接動畫

二卤唉、動畫開發(fā)

設(shè):暫停--->播放的狀態(tài)為正向,播放--->暫停為逆向涩惑。
創(chuàng)建一個坐標(biāo)系,這里設(shè)整個容器的邊長是a桑驱,坐標(biāo)系的原點為容器的左上角竭恬。

坐標(biāo)系效果圖

1、豎線的準(zhǔn)備和慣性動畫

為了更清晰的觀察熬的,預(yù)備動畫和其他動畫間做了1s的延時萍聊。

預(yù)備動畫

這個需要多看幾遍≡梦觯可以看出來:
左側(cè)豎線是經(jīng)歷了兩個過程:
第一個動畫:底部不變寿桨,上部分變短;第二個動畫:整體向上位移。
右側(cè)豎線也有兩個過程:
第一個動畫:整體向上位移亭螟;第二個動畫:底部不變挡鞍,頂部縮短。

起始狀態(tài) 第一個過程結(jié)束狀態(tài) 第二個過程結(jié)束狀態(tài)
1-1.png
1-2.png
1-3.png

根據(jù)上圖预烙,設(shè)左側(cè)豎線為L1,方向由上至下,右側(cè)豎線為L2方向由下至上墨微,通過貝塞爾曲線繪制豎線,需要確定豎線的起點和終點扁掸。
L1 起始位置起點為:(0.2a,0)終點為:(0.2a,a)
L1 第二個位置起點為:(0.2a,0.4a)終點為:(0.2a,a)
L1 第三個位置起點為:(0.2a,0.2a)終點為:(0.2a,0.8a)

L2 起始狀態(tài)起點為:(0.8a,a)終點為:(0.8a,0)
L2 第二個位置起點為:(0.8a,0.8a)終點為:(0.8a,-0.2a)
L2 第三個位置起點為:(0.8a,0.8a)終點為:(0.8a,0.2a)

準(zhǔn)備動畫就是將豎線居中變短的過程翘县,設(shè)豎線動畫總時長為:positionDuration
代碼如下:

//暫停-》播放豎線動畫
- (void)linePositiveAnimation {
    CGFloat a = self.bounds.size.width;
    
    //左側(cè)縮放動畫
    UIBezierPath *leftPath1 = [UIBezierPath bezierPath];
    [leftPath1 moveToPoint:CGPointMake(0.2*a,0.4*a)];
    [leftPath1 addLineToPoint:CGPointMake(0.2*a,a)];
    _leftLineLayer.path = leftPath1.CGPath;
    [_leftLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil];
    
    //右側(cè)豎線位移動畫
    UIBezierPath *rightPath1 = [UIBezierPath bezierPath];
    [rightPath1 moveToPoint:CGPointMake(0.8*a, 0.8*a)];
    [rightPath1 addLineToPoint:CGPointMake(0.8*a,-0.2*a)];
    _rightLineLayer.path = rightPath1.CGPath;
    [_rightLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,  positionDuration/2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){
        //左側(cè)位移動畫
        UIBezierPath *leftPath2 = [UIBezierPath bezierPath];
        [leftPath2 moveToPoint:CGPointMake(a*0.2,a*0.2)];
        [leftPath2 addLineToPoint:CGPointMake(a*0.2,a*0.8)];
        _leftLineLayer.path = leftPath2.CGPath;
        [_leftLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil];
        
        //右側(cè)豎線縮放動畫
        UIBezierPath *rightPath2 = [UIBezierPath bezierPath];
        [rightPath2 moveToPoint:CGPointMake(a*0.8,a*0.8)];
        [rightPath2 addLineToPoint:CGPointMake(a*0.8,a*0.2)];
        _rightLineLayer.path = rightPath2.CGPath;
        [_rightLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil];
    });
}

而結(jié)束時的慣性動畫,無非就是豎線由短邊長的過程谴分,和預(yù)備動畫大同小異锈麸。

代碼如下:

//播放-》暫停豎線動畫
- (void)lineInverseAnimation {
    CGFloat a = self.bounds.size.width;
    //左側(cè)位移動畫
    UIBezierPath *leftPath1 = [UIBezierPath bezierPath];
    [leftPath1 moveToPoint:CGPointMake(0.2*a,0.4*a)];
    [leftPath1 addLineToPoint:CGPointMake(0.2*a,a)];
    _leftLineLayer.path = leftPath1.CGPath;
    [_leftLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil];
    
    //右側(cè)豎線位移動畫
    UIBezierPath *rightPath1 = [UIBezierPath bezierPath];
    [rightPath1 moveToPoint:CGPointMake(0.8*a, 0.8*a)];
    [rightPath1 addLineToPoint:CGPointMake(0.8*a,-0.2*a)];
    _rightLineLayer.path = rightPath1.CGPath;
    [_rightLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,  positionDuration/2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){
        //左側(cè)豎線縮放動畫
        UIBezierPath *leftPath2 = [UIBezierPath bezierPath];
        [leftPath2 moveToPoint:CGPointMake(a*0.2,0)];
        [leftPath2 addLineToPoint:CGPointMake(a*0.2,a)];
        _leftLineLayer.path = leftPath2.CGPath;
        [_leftLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil];
        
        //右側(cè)豎線縮放動畫
        UIBezierPath *rightPath2 = [UIBezierPath bezierPath];
        [rightPath2 moveToPoint:CGPointMake(a*0.8,a)];
        [rightPath2 addLineToPoint:CGPointMake(a*0.8,0)];
        _rightLineLayer.path = rightPath2.CGPath;
        [_rightLineLayer addAnimation:[self pathAnimationWithDuration:positionDuration/2] forKey:nil];
    });
}

2、銜接動畫

2-1牺蹄、 原理分析

為了方便觀察忘伞,銜接動畫時長再放慢一倍

放慢后效果

分析:在銜接動畫中,左側(cè)豎線是連接著弧線和三角形的沙兰,右側(cè)豎線連接著是底部的弧線氓奈。而且在整個過程中,只有三角形的動畫時貫穿全程的鼎天,時長最長舀奶,所以設(shè)整個銜接動畫總時長為animationDuration,也就是三角動畫的時長斋射。

正向動畫過程:
1育勺、三角形的動畫時全程執(zhí)行的,從開始一直到結(jié)束绩鸣;
2怀大、開始時同時變化的是右側(cè)的豎線纱兑,縮短到起點呀闻;
3、弧線是連接右側(cè)豎線和左側(cè)豎線的橋梁潜慎,當(dāng)右側(cè)豎線長度縮短為零時捡多,弧線運動到左側(cè)豎線的底部;這時開始縮短弧線铐炫,至左側(cè)豎線的底部垒手;
4、左側(cè)豎線在弧線縮短至其底部時也開始縮短倒信,最終長度縮短為零科贬,同時三角形繪制完成。

逆向過程:
仔細(xì)觀察可知,逆向動畫起始就是正向動畫的逆過程榜掌。
無論在繪制直線還是曲線的時候优妙,都是利用CAShapeLayer+UIBezierPath來實現(xiàn)的,而合理的利用strokeEnd屬性就可以實現(xiàn)正向和逆向動畫的互相轉(zhuǎn)換憎账。

動畫時長:
通過三角形的動畫時長可以來確定其他部分的時長:
右側(cè)豎線縮短時長:animationDuration/4
弧線縮放時長(兩次):animationDuration/4
左側(cè)豎線縮放時長:animationDuration/4
這樣四次動畫的時長加起來正好為三角動畫的時長套硼。

2-2、繪制三角形

三角形路線

通過圖可以看出來胞皱,三角形是順時針拐了三次彎回到的原點邪意。經(jīng)過了 A->B->C->D->A 五個點完成首尾相接的三角形。
這幾個點的坐標(biāo)分別為:(0.2a,0.2a)反砌、(0.2a,0)雾鬼、(a,0.5a)、(0.2a,a)于颖、(0.2a,0.2a)

繪制三角形代碼如下:

- (void)addTriangleLayer {
    CGFloat a = self.bounds.size.width;
    
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(a*0.2,a*0.2)];
    [path addLineToPoint:CGPointMake(a*0.2,0)];
    [path addLineToPoint:CGPointMake(a,a*0.5)];
    [path addLineToPoint:CGPointMake(a*0.2,a)];
    [path addLineToPoint:CGPointMake(a*0.2,a*0.2)];
    
    _triangleLayer = [CAShapeLayer layer];
    _triangleLayer.path = path.CGPath;
    _triangleLayer.fillColor = [UIColor clearColor].CGColor;
    _triangleLayer.strokeColor = [UIColor redColor].CGColor;
    _triangleLayer.lineWidth = [self lineWidth];
    _triangleLayer.lineCap = kCALineCapButt;
    _triangleLayer.lineJoin = kCALineJoinRound;
    _triangleLayer.strokeEnd = 0;
    [self.layer addSublayer:_triangleLayer];
}

2-3呆贿、 繪制弧線

弧線示意圖

由圖可知,弧線是從右側(cè)豎線的底部森渐,連接到左側(cè)豎線的底部做入;即從E點繞圓心O順時針到F點,角度為π。

代碼如下:

- (void)addCircleLayer {
    
    CGFloat a = self.bounds.size.width;
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(a*0.8,a*0.8)];
    [path addArcWithCenter:CGPointMake(a*0.5, a*0.8) radius:0.3*a startAngle:0 endAngle:M_PI clockwise:true];
    
    _circleLayer = [CAShapeLayer layer];
    _circleLayer.path = path.CGPath;
    _circleLayer.fillColor = [UIColor clearColor].CGColor;
    _circleLayer.strokeColor = [UIColor blueColor].CGColor;
    _circleLayer.lineWidth = [self lineWidth];
    _circleLayer.lineCap = kCALineCapRound;
    _circleLayer.lineJoin = kCALineJoinRound;
    _circleLayer.strokeEnd = 0;
    [self.layer addSublayer:_circleLayer];
}

2-4同衣、 執(zhí)行動畫

在這個過程中動畫都是通過改變strokeEnd屬性實現(xiàn)的動畫竟块,為了方便,寫了一個通用的方法:

- (CABasicAnimation *)strokeEndAnimationFrom:(CGFloat)fromValue to:(CGFloat)toValue onLayer:(CALayer *)layer name:(NSString*)animationName duration:(CGFloat)duration delegate:(id)delegate {
    CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    strokeEndAnimation.duration = duration;
    strokeEndAnimation.fromValue = @(fromValue);
    strokeEndAnimation.toValue = @(toValue);
    strokeEndAnimation.fillMode = kCAFillModeForwards;
    strokeEndAnimation.removedOnCompletion = NO;
    [strokeEndAnimation setValue:animationName forKey:@"animationName"];
    strokeEndAnimation.delegate = delegate;
    [layer addAnimation:strokeEndAnimation forKey:nil];
    return strokeEndAnimation;
}

執(zhí)行正向動畫代碼如下:
(弧線動畫一部分是通過改變strokeStart屬性實現(xiàn)的)

- (void)actionPositiveAnimation {
    //開始三角動畫
    [self strokeEndAnimationFrom:0 to:1 onLayer:_triangleLayer name:TriangleAnimation duration:animationDuration delegate:self];
    //開始右側(cè)線條動畫
    [self strokeEndAnimationFrom:1 to:0 onLayer:_rightLineLayer name:RightLineAnimation duration:animationDuration/4 delegate:self];
    //開始畫弧動畫
    [self strokeEndAnimationFrom:0 to:1 onLayer:_circleLayer name:nil duration:animationDuration/4 delegate:nil];
    //開始逆向畫弧動畫
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,  animationDuration*0.25 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){
        [self circleStartAnimationFrom:0 to:1];
    });
    //開始左側(cè)線條縮短動畫
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,  animationDuration*0.5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){
        [self strokeEndAnimationFrom:1 to:0 onLayer:_leftLineLayer name:nil duration:animationDuration/2 delegate:nil];
    });
}

逆向動畫就是正向的逆過程耐齐,也就是strokeEnd屬性0到1和1到0的關(guān)系罷了浪秘,只是執(zhí)行時間不太一樣,就不貼代碼了埠况。

銜接動畫最終效果如下:

銜接動畫最終效果

延伸閱讀:優(yōu)酷播放按鈕動畫解析

Github:https://github.com/mengxianliang/XLPlayButton

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耸携,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子辕翰,更是在濱河造成了極大的恐慌夺衍,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喜命,死亡現(xiàn)場離奇詭異沟沙,居然都是意外死亡,警方通過查閱死者的電腦和手機壁榕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門矛紫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人牌里,你說我怎么就攤上這事颊咬。” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵喳篇,是天一觀的道長缓呛。 經(jīng)常有香客問我,道長杭隙,這世上最難降的妖魔是什么哟绊? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮痰憎,結(jié)果婚禮上票髓,老公的妹妹穿的比我還像新娘。我一直安慰自己铣耘,他們只是感情好洽沟,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蜗细,像睡著了一般裆操。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上炉媒,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天踪区,我揣著相機與錄音,去河邊找鬼吊骤。 笑死缎岗,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的白粉。 我是一名探鬼主播传泊,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鸭巴!你這毒婦竟也來了眷细?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鹃祖,失蹤者是張志新(化名)和其女友劉穎溪椎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惯豆,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡池磁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年奔害,在試婚紗的時候發(fā)現(xiàn)自己被綠了楷兽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡华临,死狀恐怖芯杀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤揭厚,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布却特,位于F島的核電站,受9級特大地震影響筛圆,放射性物質(zhì)發(fā)生泄漏裂明。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一太援、第九天 我趴在偏房一處隱蔽的房頂上張望闽晦。 院中可真熱鬧,春花似錦提岔、人聲如沸仙蛉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荠瘪。三九已至,卻和暖如春赛惩,著一層夾襖步出監(jiān)牢的瞬間哀墓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工喷兼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留麸祷,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓褒搔,卻偏偏與公主長得像阶牍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子星瘾,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

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

  • 一走孽、動畫過程分析 1、拆分動畫 正常動畫效果如下: 2琳状、總結(jié) 動畫是由四部分組成: 1磕瓷、直線的縮放2、弧線的縮放3...
    孟憲亮閱讀 2,412評論 18 32
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,133評論 25 707
  • 1. 關(guān)于診斷X線機準(zhǔn)直器的作用念逞,錯誤的是()困食。 (6.0 分) A. 顯示照射野 B. 顯示中心線 C. 屏蔽多...
    我們村我最帥閱讀 10,404評論 0 5
  • 我是農(nóng)民的兒子 徐 宏 土地只對農(nóng)民的兒子 敘說鄉(xiāng)情 泥土深處 那沉穩(wěn)結(jié)實的語言 來也濕漉漉 去也...
    sunxuhong閱讀 369評論 9 3
  • 慢慢彎下硕盹,腰身 奶奶是一棵蘆葦 滿頭白發(fā),垂向大地 奶奶想不起叨咖,背馱了多少年 更記不清有多少個冬 看不見頭頂?shù)奶?...
    女月亮閱讀 381評論 2 5