当我设置持续时间非常小时,CAShapeLayer动画闪光

时间:2016-04-12 10:30:06

标签: objective-c macos animation

我希望实现循环进度,并且可以动态设置进度值。代码如下:

- (CGPoint)center {
  return CGPointMake(self.view.frame.origin.x + self.view.frame.size.width / 2.0,
                 self.view.frame.origin.y + self.view.frame.size.height / 2.0);
}

- (void)viewDidLoad {
  [super viewDidLoad];

  // Do any additional setup after loading the view.

  CGMutablePathRef roundPath = CGPathCreateMutable();
  CGPathAddArc(roundPath, NULL, self.center.x, self.center.y,
           20,
           2 * M_PI + M_PI_2,
           M_PI_2,
           YES);

  CAShapeLayer *backgroundLayer = [CAShapeLayer layer];
  backgroundLayer.frame = self.view.layer.bounds;
  backgroundLayer.path = roundPath;
  backgroundLayer.strokeColor = [[NSColor blueColor] CGColor];
  backgroundLayer.fillColor = nil;
  backgroundLayer.lineWidth = 10.0f;
  backgroundLayer.lineJoin = kCALineJoinBevel;
  [self.view.layer addSublayer:backgroundLayer];

  CAShapeLayer *pathLayer = [CAShapeLayer layer];
  pathLayer.frame = self.view.layer.bounds;
  pathLayer.path = roundPath;
  pathLayer.strokeColor = [[NSColor whiteColor] CGColor];
  pathLayer.fillColor = nil;
  pathLayer.lineWidth = 10.0f;
  pathLayer.lineJoin = kCALineJoinBevel;
  [self.view.layer addSublayer:pathLayer];

  self.pathLayer = pathLayer;
  [self start];
}

- (void)start {
  CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
  pathAnimation.duration = 0.01;
  pathAnimation.fromValue = [NSNumber numberWithFloat:self.progress];
  pathAnimation.toValue = [NSNumber numberWithFloat:self.progress+0.01];
  [self.pathLayer setStrokeEnd:self.progress + 0.01];
  [pathAnimation setDelegate:self];
  [self.pathLayer addAnimation:pathAnimation forKey:@"strokeEndAnimation"];
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
  self.progress += 0.01;
  if (self.progress < 1.0) {
  [self start];
  }
}

我发现当我将持续时间设置为0.1f甚至更大时,它会正常工作。但是如果我将持续时间设置为0.01f,则动画将不会从正确的值开始,它将从更大的值开始动画减少到正确的值。所以整个动画总是闪现,任何人都有同样的问题或知道为什么?非常感谢!

2 个答案:

答案 0 :(得分:3)

originaluser2可能是关于具体原因的,但这种设计是不正确的。

在60fps时,一帧为0.0167s。您要求在不到一帧的情况下为更改设置动画。每个动画都有自己的媒体时间(kCAMediaTimingFunctionDefault),这意味着您可以通过动画创建复杂的上升/下降速度。无论如何,你的时间安排都会变得不稳定,因为你在每一步都会遇到一点错误(animationDidStop可能需要稍微不同的时间来运行,具体取决于很多因素)。动画的全部意义在于你不需要做这种事情。这就是你有动画引擎的原因。

在您想要的时间内动画到progress。不要尝试注入许多额外的动画步骤。注入步骤是动画引擎的作用。

无论如何,

CALayer旨在为您完成大部分内容。你不需要为此创建明确的动画;只是使用隐含。像(未经测试,未编译)的东西:

- (void)setProgress: (CGFloat)progress {
    double velocity = 1.0;
    [CATransaction begin];
    double delta = fabs(_progress - progress);
    [CATransaction setAnimationDuration: delta * velocity];
    [self.pathLayer setStrokeEnd: progress];
    [CATransaction commit];
}

答案 1 :(得分:1)

作为Rob says,添加重复动画并不是最好的主意。他的解决方案可能很适合你。但是,如果您仍然坚持使用重复的动画,请修改当前代码:

问题是您在添加动画之前调用了这行代码

[self.pathLayer setStrokeEnd:self.progress + 0.01];

这将在图层上创建隐式动画,因此当您添加显式动画时 - 它会导致问题(您正在观察的闪光)。

解决方案是在启动动画后更新CATransaction 中的模型层。您还需要将disableActions设置为YES,以防止生成隐式动画。

例如:

- (void)start {
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    pathAnimation.duration = 0.01;
    pathAnimation.fromValue = [NSNumber numberWithFloat:self.progress];
    pathAnimation.toValue = [NSNumber numberWithFloat:self.progress+0.01];
    [pathAnimation setDelegate:self];
    [self.pathLayer addAnimation:pathAnimation forKey:@"strokeEndAnimation"];

    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    [self.pathLayer setStrokeEnd:self.progress + 0.01];
    [CATransaction commit];
}

虽然,值得注意的是,您只需使用CATransactionstrokeEnd的隐式动画即可创建动画。

例如:

- (void)start {

    [CATransaction begin];
    [CATransaction setAnimationDuration:0.01];
    [CATransaction setCompletionBlock:^{
        self.progress += 0.01;

        if (self.progress < 1.0) {
            [self start];
        }
    }];
    [self.pathLayer setStrokeEnd:self.progress + 0.01];
    [CATransaction commit];
}

这样你就不必在每次迭代时创建一个新的显式动画。