1

我试图让用户改变我的动画速度。我正在制作一个带有贝塞尔路径的 CAKeyframeAnimation,我可以让它正确显示和运行。我尝试通过创建具有不同持续时间的新动画路径来改变速度。飞机回到起点(我还没有尝试修复)并加速。他们被绘制的路径在动画永远不会改变速度的时候消失了。当平面完成时,另一个出现在动画最初暂停的点。我不知道我做错了什么。我的问题类似于动态修改 CAKeyframeAnimation 的持续时间的问题,但我不明白 OP 关于最终使用块的说法。

//The first two methods are in a class subclassing UIView
/** Pause each plane's animation */
- (void)pauseAnimation
{    
    CFTimeInterval pausedTime = [[self layer] convertTime:CACurrentMediaTime() fromLayer:nil];
    [self layer].speed = 0.0;
    [self layer].timeOffset = pausedTime;
}

/** Resume each plane's animation */
- (void)resumeAnimation
{
    CFTimeInterval pausedTime = [[self layer] timeOffset];
    [self layer].speed = 1.0;
    [self layer].timeOffset = 0.0;
    CFTimeInterval timeSincePause = [[self layer] convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;

    for(SEPlane *plane in planes){
        plane.planeAnimationPath.speedMultiplier = 5;
        [plane.planeAnimationPath beginAnimation:self];
    }
    //[self layer].beginTime = timeSincePause;
}

//This method is in the class of planeAnimationPath
/** Begin animating plane along given path */
- (void)beginAnimation:(UIView *) view
{
    planeAnimation = nil;
    // Create animation layer for animating plane
    planeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];    
    planeAnimation.path = [bezierPath CGPath];
    planeAnimation.duration = approximateLength/(ANIMATION_SPEED * self.speedMultiplier);
    planeAnimation.calculationMode = kCAAnimationPaced;
    planeAnimation.fillMode = kCAFillModeForwards;
    planeAnimation.rotationMode = kCAAnimationRotateAuto;
    planeAnimation.removedOnCompletion = YES;
    [planeAnimation setDelegate:self];    

    // Add animation to image-layer
    [imageLayer addAnimation:planeAnimation forKey:animationKey];

    // Add image-layer to view
    [[view layer] addSublayer:imageLayer];
}
4

1 回答 1

2

与从当前位置动画到目标位置的默认动画不同,CAKeyframeAnimations 不会(据我所知)。此外,您将如何解释当前位置不在路径上的动画?

我能想到的最简单的选择是在 speedMultiplier 的设置器中执行以下操作:

  1. 使用所需路径创建一个新动画。
  2. 将持续时间设置为好像 speedMultiplier 为 1
  3. 将速度设置为 speedMultiplier
  4. 将 timeOffset 设置为 duration * "新动画的百分比已经完成"
  5. 将动画添加到图层。

正如您可能已经猜到的那样,棘手的部分是第 4 步。对于简单的路径,这很容易,但对于任意路径,它会变得有点复杂。作为起点,您将需要贝塞尔二次曲线和三次曲线的公式。搜索“贝塞尔曲线的距离参数化”,你会发现很多东西。

这是一个简单矩形路径的代码示例。该窗口只有一个 MPView 和一个滑块:

@implementation MPView {

    IBOutlet NSSlider *_slider;  // Min=0.0, Max=5.0

    CALayer  *_hostLayer;
    CALayer  *_ballLayer;

    CAKeyframeAnimation *_ballPositionAnimation;

    double _speed;
}

- (void) awakeFromNib
{
    CGRect bounds = self.bounds;

    [CATransaction begin];
    [CATransaction setDisableActions:YES];

    _speed = 1.0;

    _hostLayer = [CALayer layer];
    _hostLayer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
    self.layer = _hostLayer;
    self.wantsLayer = YES;

    _ballLayer = [CALayer layer];
    _ballLayer.bounds = CGRectMake(0, 0, 32, 32);
    _ballLayer.position = CGPointMake(40, 40);
    _ballLayer.backgroundColor = CGColorGetConstantColor(kCGColorWhite);
    _ballLayer.cornerRadius = 16;

    _hostLayer.sublayers = @[_ballLayer];


    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, NULL, _ballLayer.position.x, _ballLayer.position.y);
    CGPathAddRect(path, NULL, CGRectInset(bounds, 40, 40));
    CGPathCloseSubpath(path);

    _ballPositionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    _ballPositionAnimation.path = path;
    _ballPositionAnimation.duration = 6;
    _ballPositionAnimation.repeatCount = HUGE_VALF;

    CGPathRelease(path);

    [_ballLayer addAnimation:_ballPositionAnimation forKey:_ballPositionAnimation.keyPath];

    [CATransaction commit];

    [_slider bind:NSValueBinding toObject:self withKeyPath:@"speed" options:@{NSContinuouslyUpdatesValueBindingOption:@YES}];
}

- (double) speed
{
    return _speed;
}

- (void) setSpeed:(double)speed
{
    _speed = speed;

    CGPoint pos = [(CALayer*)_ballLayer.presentationLayer position];

    [CATransaction begin];
    [CATransaction setDisableActions:YES];

    _ballPositionAnimation.speed = _speed;
    _ballPositionAnimation.duration = 5.0;
    _ballPositionAnimation.timeOffset = _ballPositionAnimation.duration * [self percentOfPathCompleted:pos];
    [_ballLayer addAnimation:_ballPositionAnimation forKey:_ballPositionAnimation.keyPath];

    [CATransaction commit];
}

- (double) percentOfPathCompleted:(CGPoint)p
{
    CGRect rect = CGRectInset(self.bounds, 40, 40);
    double minX = NSMinX(rect);
    double minY = NSMinY(rect);
    double maxX = NSMaxX(rect);
    double maxY = NSMaxY(rect);
    double offset = 0.0;

    if (p.x == minX && p.y == minY)
        return 0.0;
    else if (p.x > minX && p.y == minY)
        offset = (p.x - minX) / rect.size.width * 0.25;
    else if (p.x == maxX && p.y < maxY)
        offset = (p.y - minY) / rect.size.height * 0.25 + 0.25;
    else if (p.x > minX && p.y == maxY)
        offset = (1.0 - (p.x - minX) / rect.size.width) * 0.25 + 0.50;
    else
        offset = (1.0 - (p.y - minY) / rect.size.height) * 0.25 + 0.75;

    NSLog(@"Offset = %f",offset);
    return offset;
}

@end
于 2013-06-02T16:37:04.720 回答