iOS CAReplicatorLayer 簡單動畫

代碼地址以下:
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;
知識補充

在進行實例以前,若是你們對UIBezierPathCAAnimation不太瞭解的,能夠先看看我前面寫的關於這兩個的文章iOS 之UIBezierPathiOS 核心動畫 Core Animation淺談code

實戰

下面咱們先看一組效果圖,這是我簡單寫的幾個例子orm

CAReplicatorLayer1.gif

項目文件截圖

分析

就上面的效果,咱們先拿其中一個進行舉例說明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 簡單解決

在接觸到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大師代發,拒絕轉載,轉載須要做者受權

相關文章
相關標籤/搜索