简体   繁体   English

如何更改CALayer动画的方向

[英]How to change direction of CALayer animation

How to change direction and start position of CALayer animation? 如何更改CALayer动画的方向和起始位置?

For example, if I animate a circle shape using following code: 例如,如果我使用以下代码制作圆形动画:

-(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;
}

Animation always starts at 3 o'clock and animates in a clockwise direction. 动画总是从3点开始,并沿顺时针方向进行动画处理。

How do I force it to start at 12 o'clock (or any other position) and animate in a counterclockwise direction ? 如何强制它从12点(或其他任何位置)开始并沿逆时针方向进行动画处理?

What is happening 怎么了

When you create an oval in a rectangle (ether using UIBezierPath of the CGPath functions) it creates an oval or ellipse that fits the specified rectangle. 当您在矩形中创建椭圆(使用CGPath函数的UIBezierPath一起)时,它将创建一个适合指定矩形的椭圆或椭圆。 This means that the center of the oval is not the same as the origin of the rectangle: 这意味着椭圆形的中心与矩形的原点不同:

矩形中的椭圆形

The animation always starts at 3 o'clock and goes counter clockwise because that is the arcing path that is a circle from 0 to 2π in the default coordinate system: 动画始终从3点开始,并逆时针旋转,因为在默认坐标系中,这是从0到2π的圆的圆弧路径:

默认坐标系中的角度

This means that the path you are animating the stroke of really looks like this: 这意味着您正在为笔画设置动画的路径如下所示:

在此处输入图片说明

The wrong way to solve the problem 解决问题的错误方法

Looking at the path, you could move the start point by rotating the view. 查看路径,您可以通过旋转视图来移动起点。 This is not recommended because it has side effects. 不建议这样做,因为它有副作用。 If there are any sublayers, those will be rotated as well. 如果有任何子层,这些子层也将被旋转。 The stroke direction could be changed either by flipping the view (scaling negative along one axis) or by animating the start of the stroke from 1 to 0 instead of the end of the stroke. 可以通过翻转视图(沿一个轴缩放为负数)或通过将笔划的起点从1动画化为0而不是动画的终点来改变笔划方向。

All of theses solutions will work, but has side effects and is less clear that what is preferable. 所有这些解决方案都可以使用,但是有副作用,并且不清楚哪种方法更可取。

A better solution 更好的解决方案

I like to look at the problem like this: the animation code is doing the right thing, animating the stroke from nothing to all of it, but the start point and stroke direction of the path is not what's expected. 我喜欢看这样的问题:动画代码做正确的事,使笔触从无到有动画化,但是路径的起点和笔触方向并不是所期望的。 This means that the "problem" lies in the path. 这意味着“问题”在于道路。

Instead of using bezierPathWithOvalInRect: , you can create your own full circle using an arc that goes from any start angle to any end angle in either a clockwise or counter clockwise direction. 代替使用bezierPathWithOvalInRect:您可以使用圆弧创建自己的完整圆,该圆弧可以沿顺时针方向或逆时针方向从任意起始角度到任意终止角度。 All that's required is that you calculate the center of the circle (and the start and end angles). 所需要做的就是计算圆心(以及起始和终止角度)。 Looking at the default coordinate system, we can see that the start angle is 3π/2 or -π/2. 查看默认坐标系,我们可以看到起始角度为3π/ 2或-π/ 2。 The end angle is then plus or minus 2π from the start angle (depending on if it's clockwise or counter clockwise). 然后,终止角度为起始角度的正负2π(取决于它是顺时针还是逆时针)。

Creating an arc like this could look something like this: 创建这样的弧可能看起来像这样:

- (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];
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM