代碼地址以下:
http://www.demodashi.com/demo/11601.htmlhtml
寫在最前面,最近在看學習的時候,偶然間發現一個沒有用過的Layer
,因而抽空研究了下,原本應該能提早記錄下來,可是苦逼的碼農須要租房子,因此耽擱了幾天,可是堅持就是勝利,下面就來看看這個強大的CAReplicatorLayer
,經過這個,能夠作不少炫酷的動畫,能省不少步驟。數組
CAReplicatorLayer
主要是爲了高效生成許多類似的圖層,能夠複製本身子層的layer,而且複製出來的layer和原生子層有一樣的屬性,位置,形變,動畫。學習
查看API咱們能夠看到有一下參數動畫
//拷貝的個數,包括自身 默認爲1 @property NSInteger instanceCount; //是否開啓景深 @property BOOL preservesDepth; //拷貝的layer執行動畫的間隔時間 @property CFTimeInterval instanceDelay; //拷貝的layer執行的3D變換 在前一個的基礎上 @property CATransform3D instanceTransform; //拷貝的layer的顏色變換 @property(nullable) CGColorRef instanceColor; //顏色偏移參數 @property float instanceRedOffset; @property float instanceGreenOffset; @property float instanceBlueOffset; //透明度偏移參數 @property float instanceAlphaOffset;
在進行實例以前,若是你們對UIBezierPath
和CAAnimation
不太瞭解的,能夠先看看我前面寫的關於這兩個的文章iOS 之UIBezierPath和iOS 核心動畫 Core Animation淺談code
下面咱們先看一組效果圖,這是我簡單寫的幾個例子orm
就上面的效果,咱們先拿其中一個進行舉例說明htm
就拿這個有20個橙色圓圈的動畫來講,以前我也有寫個,可是那個時候並不瞭解CAReplicatorLayer
,就用的比較麻煩的辦法,下面先看看以前的代碼對象
- (void)setupAnimationInLayer:(CALayer *)layer withSize:(CGFloat)size tintColor:(UIColor *)tintColor { NSTimeInterval beginTime = CACurrentMediaTime(); //小圓圈的大小 CGFloat circleSize = size/4.0; CGFloat startY = (layer.bounds.size.height - size)/2.0; CGFloat startX = (layer.bounds.size.width - size)/2.0; CGSize layerSize = layer.bounds.size; CGFloat offeset = (size/2 - circleSize/2) * sinf(M_PI_4); NSArray *rectArray = @[[NSValue valueWithCGRect:CGRectMake(layerSize.width/2 - circleSize/2, startY, circleSize, circleSize)], [NSValue valueWithCGRect:CGRectMake(layerSize.width/2 - circleSize/2 + offeset, layerSize.height/2-offeset - circleSize/2, circleSize, circleSize)], [NSValue valueWithCGRect:CGRectMake(layerSize.width/2 - circleSize + size/2, layerSize.height/2 - circleSize/2, circleSize, circleSize)], [NSValue valueWithCGRect:CGRectMake(layerSize.width/2 - circleSize/2 + offeset, layerSize.height/2 + offeset - circleSize/2, circleSize, circleSize)], [NSValue valueWithCGRect:CGRectMake(layerSize.width/2 - circleSize/2, startY + size-circleSize, circleSize, circleSize)], [NSValue valueWithCGRect:CGRectMake(layerSize.width/2 - circleSize/2 - offeset, layerSize.height/2 + offeset - circleSize/2, circleSize, circleSize)], [NSValue valueWithCGRect:CGRectMake(startX, layerSize.height/2 - circleSize/2, circleSize, circleSize)], [NSValue valueWithCGRect:CGRectMake(layerSize.width/2 - circleSize/2 - offeset, layerSize.height/2-offeset - circleSize/2, circleSize, circleSize)]]; NSArray *begintimes = @[@(0),@(0.12),@(0.24),@(0.36),@(0.48),@(0.6),@(0.72),@(0.84)]; for (int i = 0;i < rectArray.count;i++) { NSValue *data = rectArray[i]; CGRect rect = data.CGRectValue; CALayer *sublayer = [CALayer layer]; sublayer.frame = rect; sublayer.backgroundColor = [UIColor whiteColor].CGColor; sublayer.cornerRadius = circleSize/2; CAKeyframeAnimation *transformAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"]; transformAnimation.values = @[[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 0.5, 0)],[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.0, 1.0, 0)],[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 0.5, 0)]]; CAKeyframeAnimation *opacityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"]; opacityAnimation.values = @[@(0.5),@(1.0),@(0.5)]; //keyTimes這個可選參數能夠爲對應的關鍵幀指定對應的時間點,其取值範圍爲0到1.0,keyTimes中的每個時間值都對應values中的每一幀.當keyTimes沒有設置的時候,各個關鍵幀的時間是平分的 // opacityAnimation.keyTimes CAAnimationGroup *groupAnimation = [CAAnimationGroup animation]; groupAnimation.duration = 1; groupAnimation.removedOnCompletion = NO; groupAnimation.repeatCount = HUGE_VALF; groupAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; groupAnimation.animations = @[transformAnimation,opacityAnimation]; groupAnimation.beginTime = beginTime + [begintimes[i]doubleValue]; // groupAnimation.timeOffset = [timeOffeset[i] doubleValue]; [layer addSublayer:sublayer]; [sublayer addAnimation:groupAnimation forKey:nil]; } }
在上面的代碼中,我用了一個數組rectArray
來裝後面圓圈的位置,而後在用了一個for
循環,來依次添加圓圈的layer
,而且你們注意,在代碼中我還有一個數組begintimes
,這個在後面的CAAnimationGroup
中用到了,用來間隔圓圈執行動畫。雖然總體看上去代碼並很少,可是其中比較麻煩的就是在計算座標信息上。blog
在接觸到CAReplicatorLayer
後,就不用這麼麻煩了,20個圓圈,咱們能夠經過複製instanceCount
這個來進行實現,執行的時間間隔咱們能夠經過instanceDelay
來實現,固然還有一個最重要的就是其位置。查看屬性,咱們會發現,CAReplicatorLayer
有一個屬性instanceTransform
,就是進行3D
變換,要造成一個圓形的環狀,咱們能夠對其進行Z
軸旋轉,從而達到咱們想要的效果。那麼每個所旋轉的角度是多少呢?計算一下,就是20個圓圈平分2*M_PI
,因此3D
變換的代碼應該是這樣的ci
CATransform3D transform = CATransform3DIdentity; transform = CATransform3DRotate(transform, M_PI / 10.0, 0, 0, 1);
廢話很少說,咱們來看看新的解決方案的代碼
//一串圈圈,依次變大變小 透明度也變化 - (void)ballSpinFadeAnimationLayer:(CALayer *)layer withSize:(CGSize)size tintColor:(UIColor *)tintColor { CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer]; replicatorLayer.frame = CGRectMake(0, 0, layer.frame.size.width-40, layer.frame.size.height-40); replicatorLayer.backgroundColor = [UIColor whiteColor].CGColor; [layer addSublayer:replicatorLayer]; CALayer *ballLayer = [CALayer layer]; ballLayer.frame = CGRectMake((CGRectGetWidth(replicatorLayer.frame) - 10)/2.0, 0, 10, 10); ballLayer.backgroundColor = tintColor.CGColor; ballLayer.cornerRadius = 5.0; CAKeyframeAnimation *transformAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"]; transformAnimation.values = @[[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 0.5, 0)],[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.0, 1.0, 0)],[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 0.5, 0)]]; CAKeyframeAnimation *opacityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"]; opacityAnimation.values = @[@(0.5),@(1.0),@(0.5)]; //opacityAnimation.keyTimes //keyTimes這個可選參數能夠爲對應的關鍵幀指定對應的時間點,其取值範圍爲0到1.0,keyTimes中的每個時間值都對應values中的每一幀.當keyTimes沒有設置的時候,各個關鍵幀的時間是平分的 CAAnimationGroup *groupAnimation = [CAAnimationGroup animation]; groupAnimation.duration = 1; groupAnimation.removedOnCompletion = NO; groupAnimation.repeatCount = HUGE_VALF; //勻速 groupAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; groupAnimation.animations = @[transformAnimation,opacityAnimation]; [ballLayer addAnimation:groupAnimation forKey:@""]; //繞Z軸旋轉M_PI / 10.0 下面複製20個 恰好是一圈 2*M_PI CATransform3D transform = CATransform3DIdentity; transform = CATransform3DRotate(transform, M_PI / 10.0, 0, 0, 1); [replicatorLayer addSublayer:ballLayer]; replicatorLayer.instanceCount = 20; replicatorLayer.instanceTransform = transform; replicatorLayer.instanceDelay = 0.05; }
對比之下,明顯發現簡單不少,並且思路也很清晰。
下面咱們再對第一個心形動畫進行分析一下:
這個心形動畫截圖沒有截徹底,其效果我簡單描述下,從中心最凹處每隔一個時間段吐出一個圓圈,而後每個都按照心形的軌跡進行運動。咱們就不可能經過instanceTransform
來建立軌跡,由於這個是在初始化的時候就已經建立好其位置了。因此咱們只能在其複製的layer
上想辦法。能夠這樣來思考,就是複製的layer
每隔一個時間段就開始去執行心形動畫。那麼心形動畫咱們怎麼去實現呢?因爲這是一個不規則的圖形,並且是曲線,因此咱們想到了二次貝塞爾曲線
,咱們能夠經過兩個二次貝塞爾曲線
來進行拼接。
下面咱們來看完整的代碼
//愛心類型 - (void)loveAnimationLayer:(CALayer *)layer withSize:(CGSize)size tintColor:(UIColor *)tintColor { CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer]; replicatorLayer.frame = CGRectMake(0, 0, layer.frame.size.width, layer.frame.size.height); replicatorLayer.backgroundColor = [UIColor whiteColor].CGColor; [layer addSublayer:replicatorLayer]; CALayer *lineBallLayer = [CALayer layer]; lineBallLayer.backgroundColor = tintColor.CGColor; lineBallLayer.cornerRadius = 5; lineBallLayer.frame = CGRectMake((size.width - 10)/2.0, 20, 10, 10); UIBezierPath *tPath = [UIBezierPath bezierPath]; [tPath moveToPoint:CGPointMake(size.width/2.0, 25)]; //二次貝塞爾曲線 [tPath addQuadCurveToPoint:CGPointMake(size.width/2.0, 100) controlPoint:CGPointMake(size.width/2.0 + 80, -10)]; [tPath addQuadCurveToPoint:CGPointMake(size.width/2.0, 25) controlPoint:CGPointMake(size.width/2.0 - 80, -10)]; [tPath closePath];//封閉路徑 CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; animation.path = tPath.CGPath;//根據path路徑來進行動畫 animation.duration = 8;//動畫時間 animation.repeatCount = HUGE;//一直重複動畫 [lineBallLayer addAnimation:animation forKey:@""];//key能夠不設置 [replicatorLayer addSublayer:lineBallLayer]; // replicatorLayer.instanceColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:1].CGColor; replicatorLayer.instanceGreenOffset = -0.03; // 顏色值遞減。 replicatorLayer.instanceRedOffset = -0.02; // 顏色值遞減。 replicatorLayer.instanceBlueOffset = -0.01; // 顏色值遞減。 replicatorLayer.instanceCount = 40;//複製lineBallLayer 40個 replicatorLayer.instanceDelay = 0.2;//每一個複製對象執行path路徑動畫的時間間隔 前一個和後一個之間 }
其中我對顏色也進行了遞減,這樣看到的效果更加明顯。
CAReplicatorLayer
確實是個好東西,以前孤陋寡聞了。但願對各位有用iOS CAReplicatorLayer 簡單動畫
代碼地址以下:
http://www.demodashi.com/demo/11601.html
注:本文著做權歸做者,由demo大師代發,拒絕轉載,轉載須要做者受權