创建动画图层描边

Creating animated layer stroke

我想创建这样的东西,只考虑单循环以及它如何完成一个圆并在完成时反转它:

这段代码完成了我想要的一半:

CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
    drawAnimation.duration            = 1;
    drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
    drawAnimation.toValue   = [NSNumber numberWithFloat:1.0f];

    drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

    [circleLayer addAnimation:drawAnimation forKey:@"drawCircleAnimation"];

我试过反转但没用:

[CATransaction begin];
    CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
    drawAnimation.duration            = 1;
    drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
    drawAnimation.toValue   = [NSNumber numberWithFloat:1.0f];

    drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

    //[circleLayer addAnimation:drawAnimation forKey:@"drawCircleAnimation"];
    [CATransaction setCompletionBlock:^{
        CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        animation2.duration = 1;
        animation2.fromValue = [NSNumber numberWithFloat:0.0f];
        animation2.toValue = [NSNumber numberWithFloat:1.0f];
        animation2.removedOnCompletion = NO;
        animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        [circleLayer addAnimation:animation2 forKey:@"circelBack"];

    }];

    [circleLayer addAnimation:drawAnimation forKey:@"circleFront"];

    [CATransaction commit];

问题是我无法反转动画。

问题

首先,我怀疑您将动画的关键路径弄错了。您应该首先将笔画末端设置为从 0 到 1 的动画,然后然后将笔画从 0 到 1 开始。

其次,您永远不会使用新值更新模型层 - 因此当动画完成时,该层将 'snap back' 恢复到其原始状态。对你来说,这意味着当第一个动画完成时 - strokeStart 将快速回到 0.0 - 因此反向动画看起来很奇怪。


解决方案

要更新模型图层值,您只需在 CATransaction 块中将 disableActions 设置为 YES,以防止在图层 属性 上生成隐式动画更改(不会影响显式动画)。然后,在将动画添加到图层后,您需要更新模型图层的 属性

此外,您可以重复使用 CAAnimations – 因为它们在添加到图层时被复制。因此你可以为前进和后退动画定义相同的动画,只是改变关键路径。

如果您要重复动画,您可能希望将动画定义为 ivar - 并在添加之前简单地更新关键路径。

例如,在您的 viewDidLoad:

@implementation ViewController {
    CABasicAnimation* drawAnimation;
}

- (void)viewDidLoad {
    [super viewDidLoad];

    // define your animation
    drawAnimation = [CABasicAnimation animation];
    drawAnimation.duration = 1;

    // use an NSNumber literal to make your code easier to read
    drawAnimation.fromValue = @(0.0f);
    drawAnimation.toValue   = @(1.0f);

    // your timing function
    drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];

    // kick off the animation loop
    [self animate];
}

然后您需要创建一个 animate 方法来执行动画的一次迭代。不幸的是,由于 Core Animation 不支持在一个序列中重复多个动画,您将不得不使用递归来实现您的效果。

例如:

-(void) animate {

    if (self.circleLayer.superlayer) { // check circle layer is the layer heirachy before attempting to animate

        // begin your transaction
        [CATransaction begin];

        // prevent implicit animations from being generated
        [CATransaction setDisableActions:YES];

        // reset values
        self.circleLayer.strokeEnd = 0.0;
        self.circleLayer.strokeStart = 0.0;

        // update key path of animation
        drawAnimation.keyPath = @"strokeEnd";

        // set your completion block of forward animation
        [CATransaction setCompletionBlock:^{

            // weak link to self to prevent a retain cycle
            __weak typeof(self) weakSelf = self;

            // begin new transaction
            [CATransaction begin];

            // prevent implicit animations from being generated
            [CATransaction setDisableActions:YES];

            // set completion block of backward animation to call animate (recursive)
            [CATransaction setCompletionBlock:^{
                [weakSelf animate];
            }];

            // re-use your drawAnimation, just changing the key path
            drawAnimation.keyPath = @"strokeStart";

            // add backward animation
            [weakSelf.circleLayer addAnimation:drawAnimation forKey:@"circleBack"];

            // update your layer to new stroke start value
            weakSelf.circleLayer.strokeStart = 1.0;

            // end transaction
            [CATransaction commit];

        }];

        // add forward animation
        [self.circleLayer addAnimation:drawAnimation forKey:@"circleFront"];

        // update layer to new stroke end value
        self.circleLayer.strokeEnd = 1.0;

        [CATransaction commit];
    }
}

要停止动画,您可以从超级层中删除该层 - 或者实现您自己的布尔检查以判断动画是否应该继续。


完整项目:https://github.com/hamishknight/Circle-Pie-Animation