優(yōu)酷播放按鈕動(dòng)畫解析

一、動(dòng)畫過程分析

1一铅、拆分動(dòng)畫

正常動(dòng)畫效果如下:

正常動(dòng)畫效果
操作 現(xiàn)象
放慢動(dòng)畫
可以看出動(dòng)畫是由外側(cè)的藍(lán)色部分和中間的紅色三角組成陕贮。
去掉旋轉(zhuǎn)
可以看出核心的東西就是豎和弧線的縮進(jìn)、三角形的透明度變化潘飘。
只保一側(cè)留豎線和圓弧
可以看出豎線的動(dòng)畫時(shí)長大概是圓弧動(dòng)動(dòng)畫時(shí)長的一半肮之;弧線的運(yùn)動(dòng)角度是π/2,兩個(gè)弧線正好組成一個(gè)完整的圓環(huán)卜录。
只保留三角動(dòng)畫
三角動(dòng)畫比較簡單戈擒,就是兩條半透明直線組成的三角,交叉處顏色會變深艰毒。然后添加改變其透明度的動(dòng)畫筐高。

2、總結(jié)

動(dòng)畫是由四部分組成:

1丑瞧、直線的縮放
2柑土、弧線的縮放
3、三角的透明度變化
4绊汹、整體的旋轉(zhuǎn)稽屏。

三個(gè)部分中執(zhí)行時(shí)間最長的是弧線的縮放,找到這個(gè)很重要西乖,這樣就可以確定其他動(dòng)畫的開始時(shí)間和持續(xù)時(shí)間了狐榔,這個(gè)會在下面解釋坛增。

二、動(dòng)畫開發(fā)

1荒叼、豎線動(dòng)畫

在開發(fā)這類動(dòng)畫的時(shí)候轿偎,一般是先在外面?zhèn)€框框和輔助線,幫助我們更好的完成開發(fā)被廓。
這里設(shè)整個(gè)“容器”的邊長是a坏晦,坐標(biāo)系的原點(diǎn)為容器的左上角。
設(shè)從暫停--->播放的狀態(tài)為正向,從播放--->暫停為逆向嫁乘。

效果入下:

添加坐標(biāo)系

確定好坐標(biāo)系后昆婿,我們來添加一條豎線。并且給豎線添加一個(gè)縮放的動(dòng)畫蜓斧。

分析:這個(gè)動(dòng)畫的整體只不過是從暫停狀態(tài)到播放狀態(tài)的轉(zhuǎn)換過程以及逆向過程仓蛆。所以不能單單的通過bounds和position屬性來繪制這個(gè)layer,需要用到的是CAShapeLayer+UIBezierPath來創(chuàng)建這個(gè)layer挎春,并通過改變layer的strokeEnd屬性進(jìn)行對豎線的縮放操作看疙。

設(shè)左側(cè)直線的起點(diǎn)為:(a0.2,a0.9),終點(diǎn)為:(a0.2,a0.1)
(之所以這樣設(shè)定是為了更貼近原版優(yōu)酷按鈕的效果直奋,以及計(jì)算上的方便能庆。)

- (void)addLeftLineLayer {
    CGFloat a = self.layer.bounds.size.width;
    //創(chuàng)建豎線路徑
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(a*0.2,a*0.9)];
    [path addLineToPoint:CGPointMake(a*0.2,a*0.1)];
    
    //創(chuàng)建豎線顯示層
    _leftLineLayer = [CAShapeLayer layer];
    _leftLineLayer.path = path.CGPath;
    _leftLineLayer.fillColor = [UIColor clearColor].CGColor;
    _leftLineLayer.strokeColor = BLueColor.CGColor;
    _leftLineLayer.lineWidth = [self lineWidth];
    //終點(diǎn)類型為圓形
    _leftLineLayer.lineCap = kCALineCapRound;
    //連接點(diǎn)為圓形
    _leftLineLayer.lineJoin = kCALineJoinRound;
    [self.layer addSublayer:_leftLineLayer];
}

這樣就添加了一條豎線,且豎線的起點(diǎn)在下脚线,終點(diǎn)在上搁胆,是因?yàn)閺臅和O虿シ呸D(zhuǎn)換時(shí)是向下縮放,逆向時(shí)是向上縮放邮绿。

為了代碼的簡潔性渠旁,寫了一個(gè)單獨(dú)的strokeEnd屬性動(dòng)畫方法:

/**
 執(zhí)行strokeEnd動(dòng)畫
 參數(shù)為執(zhí)行時(shí)間,起始值船逮,被添加的layer
 */
- (CABasicAnimation *)strokeEndAnimationFrom:(CGFloat)fromValue to:(CGFloat)toValue onLayer:(CALayer *)layer duration:(CGFloat)duration{
    CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    strokeEndAnimation.duration = duration;
    strokeEndAnimation.fromValue = @(fromValue);
    strokeEndAnimation.toValue = @(toValue);
    //這兩個(gè)屬性設(shè)定保證在動(dòng)畫執(zhí)行之后不自動(dòng)還原
    strokeEndAnimation.fillMode = kCAFillModeForwards;
    strokeEndAnimation.removedOnCompletion = NO;
    [layer addAnimation:strokeEndAnimation forKey:nil];
    return strokeEndAnimation;
}

在正向動(dòng)畫的過程中左側(cè)豎線strokeEnd屬性從1到0:

[self strokeEndAnimationFrom:1 to:0 onLayer:_leftLineLayer duration:animationDuration/2];

在逆向動(dòng)畫的過程中左側(cè)豎線strokeEnd屬性從0到1:

[self strokeEndAnimationFrom:0 to:1 onLayer:_leftLineLayer duration:animationDuration/2];

效果如下:

豎線動(dòng)畫

同理添加右側(cè)豎線顾腊,注意右側(cè)豎線的終點(diǎn)在下,起點(diǎn)在上挖胃。并添加動(dòng)畫后效果如下:

兩條豎線

2投慈、圓弧動(dòng)畫

再確定兩條直線后,弧線就比較容易添加了冠骄,在正向動(dòng)畫過程中:

左側(cè)弧線是從左側(cè)豎線的起點(diǎn)逆時(shí)針到右側(cè)豎線的起點(diǎn)
右側(cè)弧線是從右側(cè)豎線的起點(diǎn)順時(shí)針到左側(cè)豎線的起點(diǎn)

如下圖所示伪煤,添加左側(cè)弧線效果


圓弧動(dòng)畫

要繪制一個(gè)圓弧最好的方式是通過半徑和起始角度+結(jié)束角度這種方式來確定的。
下面看一下示意圖


示意圖

如圖所示凛辣,直角三角形的邊長為0.3a抱既、0.4a、0.5a扁誓,
所以根據(jù)反三角函數(shù)防泵,角θ為acos(0.4a/0.5a)
通過這幾點(diǎn)可以確定:
弧線的半徑為0.5a
起始角度為:CGFloat startAngle = acos(4.0/5.0) + M_PI_2;
結(jié)束角度為:CGFloat endAngle = startAngle - M_PI;

添加弧線代碼:

- (void)addLeftCircle {
    CGFloat a = self.layer.bounds.size.width;
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(a*0.2,a*0.9)];
    CGFloat startAngle = acos(4.0/5.0) + M_PI_2;
    CGFloat endAngle = startAngle - M_PI;
    [path addArcWithCenter:CGPointMake(a*0.5, a*0.5) radius:0.5*a startAngle:startAngle endAngle:endAngle clockwise:false];
    
    _leftCircle = [CAShapeLayer layer];
    _leftCircle.path = path.CGPath;
    _leftCircle.fillColor = [UIColor clearColor].CGColor;
    _leftCircle.strokeColor = LightBLueColor.CGColor;
    _leftCircle.lineWidth = [self lineWidth];
    _leftCircle.lineCap = kCALineCapRound;
    _leftCircle.lineJoin = kCALineJoinRound;
    _leftCircle.strokeEnd = 0;
    [self.layer addSublayer:_leftCircle];
}

*這里注意弧線暫停時(shí)是隱藏的蚀之,所以strokeEnd為0;

正向過程執(zhí)行strokeEnd動(dòng)畫:

[self strokeEndAnimationFrom:0 to:1 onLayer:_leftLineLayer duration:animationDuration];

逆向過程:

[self strokeEndAnimationFrom:1 to:0 onLayer:_leftCircle duration:animationDuration ];

*這里需要注意的是捷泞,弧線動(dòng)畫執(zhí)行的時(shí)長是直線動(dòng)畫的二倍足删,即整個(gè)動(dòng)畫的執(zhí)行時(shí)間就是弧線的動(dòng)畫時(shí)間animationDuration,所以直線的動(dòng)畫時(shí)間為:animationDuration/2锁右。后面的旋轉(zhuǎn)動(dòng)畫時(shí)長也是通過animationDuration確定的失受。

添加左右弧線后的動(dòng)畫效果:

3、三角動(dòng)畫

三角動(dòng)畫相對于之前的豎線和圓弧的動(dòng)畫比較簡單咏瑟,就是通過兩條直線確定一個(gè)三角形
添加這個(gè)三角形前拂到,還是先創(chuàng)建一個(gè)放置三角形的“容器”,然后通過這個(gè)容器確定三角形兩條邊的起點(diǎn)和終點(diǎn)码泞,如圖所示:


三角輔助線

如圖可以看出兄旬,三角兩條邊的起點(diǎn)為“容器”的底邊中點(diǎn),兩條邊的終點(diǎn)分別為“容器”的左上角及右上角余寥。
代碼如下:

- (void)addCenterTriangleLayer {
    CGFloat a = self.layer.bounds.size.width;
    //初始化容器
    _triangleCotainer = [CALayer layer];
    _triangleCotainer.bounds = CGRectMake(0, 0, 0.4*a, 0.35*a);
    _triangleCotainer.position = CGPointMake(a*0.5, a*0.55);
    _triangleCotainer.opacity = 0;
    _triangleCotainer.borderWidth = 1;
    [self.layer addSublayer:_triangleCotainer];
    
    //容器寬高
    CGFloat b = _triangleCotainer.bounds.size.width;
    CGFloat c = _triangleCotainer.bounds.size.height;
    
    //第一條邊
    UIBezierPath *path1 = [UIBezierPath bezierPath];
    [path1 moveToPoint:CGPointMake(0,0)];
    [path1 addLineToPoint:CGPointMake(b/2,c)];
    
    CAShapeLayer *layer1 = [CAShapeLayer layer];
    layer1.path = path1.CGPath;
    layer1.fillColor = [UIColor clearColor].CGColor;
    layer1.strokeColor = RedColor.CGColor;
    layer1.lineWidth = [self lineWidth];
    layer1.lineCap = kCALineCapRound;
    layer1.lineJoin = kCALineJoinRound;
    layer1.strokeEnd = 1;
    [_triangleCotainer addSublayer:layer1];
    
    //第二條邊
    UIBezierPath *path2 = [UIBezierPath bezierPath];
    [path2 moveToPoint:CGPointMake(b,0)];
    [path2 addLineToPoint:CGPointMake(b/2,c)];
    
    CAShapeLayer *layer2 = [CAShapeLayer layer];
    layer2.path = path2.CGPath;
    layer2.fillColor = [UIColor clearColor].CGColor;
    layer2.strokeColor = RedColor.CGColor;
    layer2.lineWidth = [self lineWidth];
    layer2.lineCap = kCALineCapRound;
    layer2.lineJoin = kCALineJoinRound;
    layer2.strokeEnd = 1;
    [_triangleCotainer addSublayer:layer2];
}

添加透明度動(dòng)畫

- (void)actionTriangleOpacityAnimationFrom:(CGFloat)from to:(CGFloat)to duration:(CGFloat)duration{
    CABasicAnimation *alphaAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    alphaAnimation.duration = duration; // 持續(xù)時(shí)間
    alphaAnimation.fromValue = @(from);
    alphaAnimation.toValue = @(to);
    alphaAnimation.fillMode = kCAFillModeForwards;
    alphaAnimation.removedOnCompletion = NO;
    [alphaAnimation setValue:@"alphaAnimation" forKey:@"animationName"];
    [_triangleCotainer addAnimation:alphaAnimation forKey:nil];
}

效果如下:

image

4领铐、旋轉(zhuǎn)動(dòng)畫

- (void)actionRotateAnimationClockwise:(BOOL)clockwise {
    //逆時(shí)針旋轉(zhuǎn)
    CGFloat startAngle = 0.0f;
    CGFloat endAngle = -M_PI_2;
    CGFloat duration = 0.75 * animationDuration;
    //順時(shí)針旋轉(zhuǎn)
    if (clockwise) {
        startAngle = -M_PI_2;
        endAngle = 0.0;
        duration = animationDuration;
    }
    CABasicAnimation *roateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    roateAnimation.duration = duration; // 持續(xù)時(shí)間
    roateAnimation.fromValue = [NSNumber numberWithFloat:startAngle];
    roateAnimation.toValue = [NSNumber numberWithFloat:endAngle];
    roateAnimation.fillMode = kCAFillModeForwards;
    roateAnimation.removedOnCompletion = NO;
    [roateAnimation setValue:@"roateAnimation" forKey:@"animationName"];
    [self.layer addAnimation:roateAnimation forKey:nil];
}

最終效果如下:

正常動(dòng)畫效果

三、小結(jié)

所有的看起來復(fù)雜的動(dòng)畫只要拆分成各個(gè)模塊都是比較簡單的宋舷,只要把各個(gè)模塊做好在拼湊到一起就可以了绪撵。

四、代碼

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

五肥缔、延伸

愛奇藝播放按鈕動(dòng)畫

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市汹来,隨后出現(xiàn)的幾起案子续膳,更是在濱河造成了極大的恐慌,老刑警劉巖收班,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坟岔,死亡現(xiàn)場離奇詭異,居然都是意外死亡摔桦,警方通過查閱死者的電腦和手機(jī)社付,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來邻耕,“玉大人鸥咖,你說我怎么就攤上這事⌒质溃” “怎么了啼辣?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長御滩。 經(jīng)常有香客問我鸥拧,道長党远,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任富弦,我火速辦了婚禮沟娱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘腕柜。我一直安慰自己济似,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布媳握。 她就那樣靜靜地躺著碱屁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蛾找。 梳的紋絲不亂的頭發(fā)上娩脾,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天,我揣著相機(jī)與錄音打毛,去河邊找鬼柿赊。 笑死,一個(gè)胖子當(dāng)著我的面吹牛幻枉,可吹牛的內(nèi)容都是我干的碰声。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼熬甫,長吁一口氣:“原來是場噩夢啊……” “哼胰挑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起椿肩,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤瞻颂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后郑象,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贡这,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年厂榛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盖矫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡击奶,死狀恐怖辈双,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柜砾,我是刑警寧澤辐马,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響喜爷,放射性物質(zhì)發(fā)生泄漏冗疮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一檩帐、第九天 我趴在偏房一處隱蔽的房頂上張望术幔。 院中可真熱鬧,春花似錦湃密、人聲如沸诅挑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拔妥。三九已至,卻和暖如春达箍,著一層夾襖步出監(jiān)牢的瞬間没龙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工缎玫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留硬纤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓赃磨,卻偏偏與公主長得像筝家,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子邻辉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

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