首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从当前CAAnimation的状态启动新的CAAnimation

从当前CAAnimation的状态启动新的CAAnimation
EN

Stack Overflow用户
提问于 2012-07-25 20:35:23
回答 2查看 5.2K关注 0票数 7

我使用一对CALayers作为图像蒙版,允许我在跟随当前触摸位置的同时,以设定的速度对灯泡填充或清空进行动画处理。也就是说,一个遮罩跳跃以跟随触摸,另一个滑动到该位置。因为我使用的是显式动画,所以在添加动画时,我不得不设置蒙版滑动蒙版的位置。这意味着,如果我开始填充,然后在填充完成之前开始空填充,则空填充将从完成的填充位置开始(相反情况也是如此)。

有没有一种方法可以获取动画的位置,在动画的每个步骤设置位置,或者让新动画从活动动画的当前状态开始?

处理动画的代码如下:

代码语言:javascript
复制
- (void)masksFillTo:(CGFloat)height {
    // Clamp the height we fill to inside the bulb. Remember Y gets bigger going down.
    height = MIN(MAX(BULB_TOP, height), BULB_BOTTOM);

    // We can find the target Y location by subtracting the Y value for the top of the
    // bulb from the height.
    CGFloat targetY = height - BULB_TOP;

    // Find the bottom of the transparent mask to determine where the solid fill
    // is sitting. Then find how far that fill needs to move.
    // TODO: This works with the new set position, so overriding old anime doesn't work
    CGFloat bottom = transMask.frame.origin.y + transMask.frame.size.height;

    // If the target is above the bottom of the solid, we want to fill up.
    // This means the empty mask jumps and the transparent mask slides.
    CALayer *jumper;
    CALayer *slider;
    if (bottom - targetY >= 0) {
        jumper = emptyMask;
        slider = transMask;

        // We need to reset the bottom to the emptyMask
        bottom = emptyMask.frame.origin.y + emptyMask.frame.size.height;
    } else {
        jumper = transMask;
        slider = emptyMask;
    }

    [jumper removeAllAnimations];
    [slider removeAllAnimations];

    CGFloat dy = bottom - targetY;

    [CATransaction begin]; 
    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
    [jumper setPosition:CGPointMake(jumper.position.x, jumper.position.y - dy)];
    [self slideMaskFillTo:height withMask:slider]; // Do this inside here or an odd flash glitch appears.
    [CATransaction commit];
}

// TODO: Always starts from new position, even if animation hasn't reached it.
- (void)slideMaskFillTo:(CGFloat)height withMask:(CALayer *)slider {
    // We can find the target Y location by subtracting the Y value for the top of the
    // bulb from the height.
    CGFloat targetY = height - BULB_TOP;

    // We then find the bottom of the mask.
    CGFloat bottom = slider.frame.origin.y + slider.frame.size.height;

    CGFloat dy = bottom - targetY;

    // Do the animation. Animating with duration doesn't appear to work properly.
    // Apparently "When modifying layer properties from threads that don’t have a runloop, 
    // you must use explicit transactions."
    CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:@"position"];
    a.duration = (dy > 0 ? dy : -dy) / PXL_PER_SEC; // Should be 2 seconds for a full fill
    a.fromValue = [NSValue valueWithCGPoint:slider.position];
    CGPoint newPosition = slider.position;
    newPosition.y -= dy;
    a.toValue = [NSValue valueWithCGPoint:newPosition];
    a.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    [slider addAnimation:a forKey:@"colorize"];

    // Update the actual position
    slider.position = newPosition;
}

这是一个如何调用的示例。请注意,这意味着它可以被称为中间动画。

代码语言:javascript
复制
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self.view];

    [self masksFillTo:point.y];
}

如果任何人发现它相关,这是图像和面具的创建。

代码语言:javascript
复制
// Instantiate the different bulb images - empty, transparent yellow, and solid yellow. This
// includes setting the frame sizes. This approach found at http://stackoverflow.com/a/11218097/264775
emptyBulb = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Light.png"]];
transBulb = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Light-moving.png"]];
solidBulb = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Light-on.png"]];

[emptyBulb setFrame:CGRectMake(10, BULB_TOP, 300, BULB_HEIGHT)]; // 298 x 280
[transBulb setFrame:CGRectMake(10, BULB_TOP, 300, BULB_HEIGHT)]; // 298 x 280
[solidBulb setFrame:CGRectMake(10, BULB_TOP, 300, BULB_HEIGHT)]; // 298 x 280

[self.view addSubview:solidBulb]; // Empty on top, then trans, then solid.
[self.view addSubview:transBulb];
[self.view addSubview:emptyBulb];

// Create a mask for the empty layer so it will cover the other layers.
emptyMask = [CALayer layer];
[emptyMask setContentsScale:emptyBulb.layer.contentsScale]; // handle retina scaling
[emptyMask setFrame:emptyBulb.layer.bounds];
[emptyMask setBackgroundColor:[UIColor blackColor].CGColor];
emptyBulb.layer.mask = emptyMask;

// Also create a mask for the transparent image.
transMask = [CALayer layer];
[transMask setContentsScale:transBulb.layer.contentsScale]; // handle retina scaling
[transMask setFrame:transBulb.layer.bounds];
[transMask setBackgroundColor:[UIColor blackColor].CGColor];
transBulb.layer.mask = transMask;
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-07-25 21:21:55

刚刚通过this answer找到了一个解决方案。如果查看文档的正确部分,您将发现以下内容:

- (id)presentationLayer

返回包含所有属性的层的副本,这些属性在当前事务开始时的状态,并应用任何活动动画。

因此,如果我在第一次检查透明蒙版位置(也就是实心级别)之前添加此代码,我将获取当前的动画位置,并可以在填充和填充之间切换。

代码语言:javascript
复制
CALayer *temp;

if (transMask.animationKeys.count > 0) {
    temp = transMask.presentationLayer;
    transMask.position = temp.position;
}

if (emptyMask.animationKeys.count > 0) {
    temp = emptyMask.presentationLayer;
    emptyMask.position = temp.position;
}
票数 11
EN

Stack Overflow用户

发布于 2019-03-27 14:48:55

每次你得到原始层时,layer.presentation()都会复制它,你需要为你拥有的每个自定义CALayer覆盖init(layer: Any)

从当前状态开始的另一种可能方法是使用当前动画的beginTime设置动画的beginTime。它允许使用偏移开始新的动画。

在您的案例中,它可能是(Swift):

代码语言:javascript
复制
if let currentAnimation = slider.animation(forKey: "colorize") {
    let currentTime = CACurrentMediaTime()
    let animationElapsedTime = currentAnimation.beginTime + currentAnimation.duration - currentTime
    a.beginTime = currentTime - animationElapsedTime
}

然而,这对于线性计时非常有效,但对于其他计时,可能很难计算适当的时间。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11649837

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档