创建动画图层描边
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];
}
}
要停止动画,您可以从超级层中删除该层 - 或者实现您自己的布尔检查以判断动画是否应该继续。
我想创建这样的东西,只考虑单循环以及它如何完成一个圆并在完成时反转它:
这段代码完成了我想要的一半:
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];
}
}
要停止动画,您可以从超级层中删除该层 - 或者实现您自己的布尔检查以判断动画是否应该继续。