如何改变CALayer动画的方向

How to change direction of CALayer animation

如何改变CALayer动画的方向和起始位置?

例如,如果我使用以下代码为圆形设置动画:

-(void)drawCircle {
    CAShapeLayer *circleShapeLayer = [[CAShapeLayer alloc] init];
    circleShapeLayer.path          = [self circleBezierPath].CGPath;
    circleShapeLayer.strokeColor   = [UIColor lightGrayColor].CGColor;
    circleShapeLayer.fillColor     = [UIColor clearColor].CGColor;
    circleShapeLayer.lineWidth     = 2;
    [self.view.layer addSublayer:circleShapeLayer];

    CABasicAnimation *circleShapeAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    circleShapeAnimation.timingFunction    = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    circleShapeAnimation.duration          = 1.0f;
    circleShapeAnimation.fromValue         = @(0.0f);
    circleShapeAnimation.toValue           = @(1.0f);
    [circleShapeLayer addAnimation:circleShapeAnimation forKey:@"strokeEnd"];
}

-(UIBezierPath *)circleBezierPath {
    UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 100, 100)];
    return circlePath;
}

动画总是从 3 点开始,并按顺时针方向进行动画。

如何强制它从 12 点钟(或任何其他位置)开始并以 逆时针方向 动画?

发生了什么

当您在矩形中创建椭圆时(使用 CGPath 函数的 UIBezierPath),它会创建适合指定矩形的椭圆。这意味着椭圆的中心与矩形的原点不同:

动画总是从3点钟开始逆时针走,因为那是默认坐标系中0到2π的圆弧路径:

这意味着您为笔划设置动画的路径看起来确实像这样:

错误的解决方法

查看路径,可以通过旋转视图来移动起点。不推荐这样做,因为它有副作用。如果有任何子图层,它们也会旋转。可以通过翻转视图(沿一个轴负缩放)或通过将笔划的起点从 1 动画化为 0 而不是笔划的终点来更改笔划方向。

所有这些解决方案都有效,但有副作用,而且不太清楚哪种方案更可取。

更好的解决方案

我喜欢这样看问题:动画代码做的是正确的,将笔划从无到有设置动画,但路径的起点和笔划方向不是预期的。这意味着 "problem" 位于路径中。

除了使用 bezierPathWithOvalInRect:,您还可以使用沿顺时针或逆时针方向从任意起始角到任意终止角的圆弧来创建自己的完整圆。所需要的只是计算圆心(以及开始和结束角度)。查看默认坐标系,我们可以看到起始角为3π/2或-π/2。然后结束角度从开始角度正负 2π(取决于它是顺时针还是逆时针)。

创建这样的圆弧可能看起来像这样:

- (UIBezierPath *)circleBezierPath
{
    CGRect boundingRect = CGRectMake(100, 100, 100, 100);
    CGPoint center      = CGPointMake(CGRectGetMidX(boundingRect), CGRectGetMidY(boundingRect));
    CGFloat radius      = MIN(CGRectGetWidth(boundingRect), CGRectGetHeight(boundingRect));

    CGFloat twelveOClockAngle = -M_PI_2;
    BOOL clockwise = NO; // NO for counter clockwise

    CGFloat startAngle = twelveOClockAngle;
    CGFloat endAngle = startAngle + (2.0*M_PI * (clockwise ? 1.0 : -1.0));

    return [UIBezierPath bezierPathWithArcCenter:center
                                          radius:radius
                                      startAngle:startAngle
                                        endAngle:endAngle
                                       clockwise:clockwise];
}