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 * "percent of new animation already complete" に設定します
  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 に答える