Slow down & accelerate animations with CoreAnimation

二次信任 提交于 2019-12-07 04:50:03

问题


I am trying to apply a slow-motion effect to my application, pretty much like how you can slow down most graphical effects of Mac OS if you press Shift.

My application uses CoreAnimation, so I thought it should be no biggie: set speed to some slower value (like 0.1) and set it back to 1 once I'm done, and here I go.

It seems, unfortunately, that this is not the right way. The slowdown works great, however when I want to get back to normal speed, it resumes as if the speed was 1 the whole time. This basically means that if I held Shift for long enough, as soon as I release it, the animation instantly completes.

I found a Technical QA page explaining how to pause and resume an animation, but I can't seem to get it right if it's not about entirely pausing the animation. I'm definitely not very good at time warping.

What would be the right way to slow down then resume an animation with CoreAnimation?

Here's the useful code:

-(void)flagsChanged:(NSEvent *)theEvent
{
    CALayer* layer = self.layer;
    [CATransaction begin];
    CATransaction.disableActions = YES;
    layer.speed = (theEvent.modifierFlags & NSShiftKeyMask) ? 0.1 : 1;
    [CATransaction commit];
}

回答1:


Tricky problem...

I set up a test with a basic UIView. I initiate an animation of its layer from it's current point to a target point.

In order to slow down the core animation, I actually had to construct and replace a new animation (since you can't modify the existing animation).

It's especially important to figure out how far along the current animation has already proceeded. This is done by accessing beginTime, currentTime, calculating the elapsed time and then figuring out how long should be the duration of the new animation.

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

Note: The above code works only in 1 direction, not with animations that autoreverse...



来源:https://stackoverflow.com/questions/9269568/slow-down-accelerate-animations-with-coreanimation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!