一、動(dòng)畫過程分析
1一铅、拆分動(dòng)畫
正常動(dòng)畫效果如下:
操作 | 現(xiàn)象 |
---|---|
放慢動(dòng)畫 |
|
去掉旋轉(zhuǎn) |
|
只保一側(cè)留豎線和圓弧 |
|
只保留三角動(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)系后昆婿,我們來添加一條豎線。并且給豎線添加一個(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];
效果如下:
同理添加右側(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è)弧線效果
要繪制一個(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];
}
效果如下:
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];
}
最終效果如下:
三、小結(jié)
所有的看起來復(fù)雜的動(dòng)畫只要拆分成各個(gè)模塊都是比較簡單的宋舷,只要把各個(gè)模塊做好在拼湊到一起就可以了绪撵。
四、代碼
Github:https://github.com/mengxianliang/XLPlayButton