我试图让用户改变动画的速度。我正在使用bezier路径制作CAKeyframeAnimation,我可以让它显示并正确运行。 我尝试通过创建具有不同持续时间的新动画路径来更改速度。飞机回到起点(我还没有尝试修理)并加快速度。它们被绘制的路径在动画永远不会改变速度时消失。当飞机完成时,另一个出现在动画首先暂停的位置。我不知道我做错了什么。我的问题类似于这个modifying dynamically the duration of 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];
}
答案 0 :(得分:2)
与从当前位置动画到目标位置的默认动画不同,CAKeyframeAnimations不会(据我所知)。除了你如何解释当前位置不在路径上的动画?
我能想到的最简单的选择是在speedMultiplier的setter中执行以下操作:
正如您可能已经猜到的,棘手的部分是第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