使用CoreAnimation减慢和加速动画速度

5
我正在尝试在我的应用程序中应用慢动作效果,就像在Mac OS上按下Shift键可以减缓大多数图形效果一样。
我的应用程序使用CoreAnimation,所以我认为这应该不是什么大问题:设置`speed`为较慢的值(如`0.1`),完成后将其设置回`1`,然后我就可以开始操作了。
不幸的是,似乎这不是正确的方法。减速效果很好,但当我想恢复正常速度时,它会像速度始终为`1`一样恢复。这基本上意味着如果我按住Shift键足够长的时间,那么一旦释放,动画将立即完成。
我找到了一个技术QA页面,说明了如何暂停和恢复动画,但如果不完全暂停动画,则无法正确执行。我绝对不擅长时间扭曲。
使用CoreAnimation如何正确地减速然后恢复动画?以下是有用的代码:
-(void)flagsChanged:(NSEvent *)theEvent
{
    CALayer* layer = self.layer;
    [CATransaction begin];
    CATransaction.disableActions = YES;
    layer.speed = (theEvent.modifierFlags & NSShiftKeyMask) ? 0.1 : 1;
    [CATransaction commit];
}

1
通常,只有在按住Shift键时启动的动画才会受到影响,松开Shift键并不会立即加速动画。这通常可以通过使动画的持续时间取决于创建动画时是否按下Shift键来实现。 - Lily Ballard
1
@Kevin Ballard,我知道;但除此之外,这是一个很酷的效果,而且在我的情况下能够随时减速也可能是_有用的_。 - zneak
我认为当你改变速度时,你还需要计算一个时间偏移量来应用,以便旧速度时间 == 新速度时间+偏移量。 - Lily Ballard
1个回答

4

棘手的问题...

我创建了一个基本的 UIView 并进行了测试。我启动了一个动画,将其层从当前位置移动到目标位置。

为了减慢核心动画的速度,实际上我必须构建并替换一个新的动画(因为不能修改现有的动画)。

特别重要的是要弄清楚当前动画已经执行了多远。这可以通过访问 beginTime、currentTime,并计算经过的时间,然后确定新动画的持续时间来完成。

- (void)initiate {
    if(!initiated) {
        [CATransaction begin];
        [CATransaction disableActions];
        [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]];
        CABasicAnimation *ba = [CABasicAnimation animationWithKeyPath:@"position"];
        ba.duration = 10.0f;
        ba.fromValue = [NSValue valueWithCGPoint:view.layer.position];
        ba.toValue = [NSValue valueWithCGPoint:CGPointMake(384, 512)];
        [view.layer addAnimation:ba forKey:@"animatePosition"];
        [CATransaction commit];
        initiated = YES;
    }
}

- (void)slow {
    [CATransaction begin];
    CABasicAnimation *old = (CABasicAnimation *)[view.layer animationForKey:@"animatePosition"];
    CABasicAnimation *ba = [CABasicAnimation animationWithKeyPath:@"position"];
    CFTimeInterval animationBegin = old.beginTime;
    CFTimeInterval currentTime = CACurrentMediaTime();
    CFTimeInterval elapsed = currentTime - animationBegin;
    ba.duration = [[old valueForKey:@"duration"] floatValue] - elapsed;
    ba.duration = [[old valueForKey:@"duration"] floatValue];
    ba.autoreverses = [[old valueForKey:@"autoreverses"] boolValue];
    ba.repeatCount = [[old valueForKey:@"repeatCount"] floatValue];
    ba.fromValue = [NSValue valueWithCGPoint:((CALayer *)[view.layer presentationLayer]).position];
    ba.toValue = [old valueForKey:@"toValue"];
    ba.speed = 0.1;
    [view.layer addAnimation:ba forKey:@"animatePosition"];
    [CATransaction commit];
}

- (void)normal {
    [CATransaction begin];
    CABasicAnimation *old = (CABasicAnimation *)[view.layer animationForKey:@"animatePosition"];
    CABasicAnimation *ba = [CABasicAnimation animationWithKeyPath:@"position"];
    CFTimeInterval animationBegin = old.beginTime;
    CFTimeInterval currentTime = CACurrentMediaTime();
    CFTimeInterval elapsed = currentTime - animationBegin;
    ba.duration = [[old valueForKey:@"duration"] floatValue] - elapsed;
    ba.autoreverses = [[old valueForKey:@"autoreverses"] boolValue];
    ba.repeatCount = [[old valueForKey:@"repeatCount"] floatValue];
    ba.fromValue = [NSValue valueWithCGPoint:((CALayer *)[view.layer presentationLayer]).position];
    ba.toValue = [old valueForKey:@"toValue"];
    ba.speed = 1;
    [view.layer addAnimation:ba forKey:@"animatePosition"];
    [CATransaction commit];
}

注意:上述代码仅适用于单向操作,而不适用于自动反向播放的动画...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接