如何使用Core Animation创建自定义缓动功能?

我在iOS中很好地沿着CGPath (QuadCurve)设置了CALayer动画效果。 但是我想使用比苹果公司提供的少数有趣的缓动功能(EaseIn / EaseOut等)。 例如,弹跳或弹性功能。

MediaTimingFunction(bezier)可以做这些事情:

但我想创建更复杂的定时功能 。 问题是媒体时机似乎需要一个立方贝塞尔,它不足以产生这些效果:

http://wiki.sparrow-framework.org/_media/manual/transitions.png

在其他框架中创建上述代码很简单,这使得这非常令人沮丧。 请注意,曲线将输入时间映射到输出时间(Tt曲线),而不是时间 - 位置曲线。 例如,easeOutBounce(T)= t返回一个新的t。 然后,t用于绘制运动(或任何我们应该动画的属性)。

所以,我想创建一个复杂的自定义CAMediaTimingFunction但我不知道如何去做,或者甚至有可能吗? 有没有其他的选择?

编辑:

以下是步骤的具体示例。 非常教育:)

  • 我想沿着从a点到b点的一条线对物体进行动画处理,但我希望它使用上面的easeOutBounce曲线沿着线“反弹”它的移动。 这意味着它将遵循从a到b的确切的线,但会以比使用当前基于bezier的CAMediaTimingFunction更加复杂的方式进行加速和减速。

  • 让我们使用CGPath指定任意曲线运动。 它仍然应该沿着该曲线移动,但它应该像在示例中一样加速和减速。

  • 理论上我认为它应该像这样工作:

    让我们将运动曲线描述为关键帧动画移动(t)= p,其中t是时间[0..1],p是在时间t计算的位置。 所以移动(0)返回曲线开始处的位置,移动(0.5)确切的中间值并结束移动(1)。 使用定时函数time(T)= t来提供移动的t值应该给我我想要的。 对于弹跳效果,定时函数应该返回时间(0.8)和时间(0.8)相同的t值(仅举例)。 只需更换计时功能即可获得不同的效果。

    (是的,可以通过创建和连接四个线段来实现线反弹,但这不是必须的,毕竟这只是一个简单的线性函数,它将时间值映射到位置。)

    我希望我在这里有意义。


    我找到了这个:

    可爱与爱 - 核心动画中的参数加速曲线

    但我认为通过使用块可以使它变得更简单,更易读。 所以我们可以在CAKeyframeAnimation上定义一个类似如下的类:

    CAKeyframeAnimation + Parametric.h:

    // this should be a function that takes a time value between 
    //  0.0 and 1.0 (where 0.0 is the beginning of the animation
    //  and 1.0 is the end) and returns a scale factor where 0.0
    //  would produce the starting value and 1.0 would produce the
    //  ending value
    typedef double (^KeyframeParametricBlock)(double);
    
    @interface CAKeyframeAnimation (Parametric)
    
    + (id)animationWithKeyPath:(NSString *)path 
          function:(KeyframeParametricBlock)block
          fromValue:(double)fromValue
          toValue:(double)toValue;
    

    CAKeyframeAnimation + Parametric.m:

    @implementation CAKeyframeAnimation (Parametric)
    
    + (id)animationWithKeyPath:(NSString *)path 
          function:(KeyframeParametricBlock)block
          fromValue:(double)fromValue
          toValue:(double)toValue {
      // get a keyframe animation to set up
      CAKeyframeAnimation *animation = 
        [CAKeyframeAnimation animationWithKeyPath:path];
      // break the time into steps
      //  (the more steps, the smoother the animation)
      NSUInteger steps = 100;
      NSMutableArray *values = [NSMutableArray arrayWithCapacity:steps];
      double time = 0.0;
      double timeStep = 1.0 / (double)(steps - 1);
      for(NSUInteger i = 0; i < steps; i++) {
        double value = fromValue + (block(time) * (toValue - fromValue));
        [values addObject:[NSNumber numberWithDouble:value]];
        time += timeStep;
      }
      // we want linear animation between keyframes, with equal time steps
      animation.calculationMode = kCAAnimationLinear;
      // set keyframes and we're done
      [animation setValues:values];
      return(animation);
    }
    
    @end
    

    现在用法看起来像这样:

    // define a parametric function
    KeyframeParametricBlock function = ^double(double time) {
      return(1.0 - pow((1.0 - time), 2.0));
    };
    
    if (layer) {
      [CATransaction begin];
        [CATransaction 
          setValue:[NSNumber numberWithFloat:2.5]
          forKey:kCATransactionAnimationDuration];
    
        // make an animation
        CAAnimation *drop = [CAKeyframeAnimation 
          animationWithKeyPath:@"position.y"
          function:function fromValue:30.0 toValue:450.0];
        // use it
        [layer addAnimation:drop forKey:@"position"];
    
      [CATransaction commit];
    }
    

    我知道它可能不像你想要的那么简单,但这是一个开始。


    从iOS 10开始,使用两个新的计时对象可以更轻松地创建自定义计时功能。

    1) UICubicTimingParameters允许定义三次Bézier曲线作为缓动函数。

    let cubicTimingParameters = UICubicTimingParameters(controlPoint1: CGPoint(x: 0.25, y: 0.1), controlPoint2: CGPoint(x: 0.25, y: 1))
    let animator = UIViewPropertyAnimator(duration: 0.3, timingParameters: cubicTimingParameters)
    

    或者简单地使用动画初始化的控制点

    let controlPoint1 = CGPoint(x: 0.25, y: 0.1)
    let controlPoint2 = CGPoint(x: 0.25, y: 1)
    let animator = UIViewPropertyAnimator(duration: 0.3, controlPoint1: controlPoint1, controlPoint2: controlPoint2) 
    

    这个令人敬畏的服务将帮助您为曲线选择控制点。

    2) UISpringTimingParameters使开发人员可以操纵阻尼比,质量,刚度和初始速度来创建所需的弹簧行为。

    let velocity = CGVector(dx: 1, dy: 0)
    let springParameters = UISpringTimingParameters(mass: 1.8, stiffness: 330, damping: 33, initialVelocity: velocity)
    let springAnimator = UIViewPropertyAnimator(duration: 0.0, timingParameters: springParameters)
    

    持续时间参数仍然存在于Animator中,但在春季时间将被忽略。

    如果这两个选项不够用,您也可以通过确认UITimingCurveProvider协议来实现自己的时序曲线。

    更多细节,如何创建具有不同时间参数的动画,可以在文档中找到。

    另请参阅WWDC 2016的UIKit动画和转换演示的进展。


    创建自定义计时函数的一种方法是使用CAMediaTimingFunction中的functionWithControlPoints ::::工厂方法(还有一个相应的initWithControlPoints :::: init方法)。 这会为您的计时功能创建一个贝塞尔曲线。 这不是一个任意的曲线,但贝塞尔曲线非常强大和灵活。 需要一点练习才能掌握控制点。 提示:大多数绘图程序可以创建贝塞尔曲线。 玩这些将会给你一个关于你用控制点表示的曲线的视觉反馈。

    这个链接指向苹果的文档。 关于如何使用曲线构建预构建函数,有一个简短而有用的部分。

    编辑:下面的代码显示了一个简单的反弹动画。 为此,我创建了一个组合的计时函数( 时间 NSArray属性),并为动画的每个段提供了不同的时间长度( 时间属性)。 通过这种方式,您可以编写Bézier曲线,为动画创作更复杂的时序。 这是一个很好的关于这种类型的动画的文章,有很好的示例代码。

    - (void)viewDidLoad {
        UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 50.0, 50.0)];
    
        v.backgroundColor = [UIColor redColor];
        CGFloat y = self.view.bounds.size.height;
        v.center = CGPointMake(self.view.bounds.size.width/2.0, 50.0/2.0);
        [self.view addSubview:v];
    
        //[CATransaction begin];
    
        CAKeyframeAnimation * animation; 
        animation = [CAKeyframeAnimation animationWithKeyPath:@"position.y"]; 
        animation.duration = 3.0; 
        animation.removedOnCompletion = NO;
        animation.fillMode = kCAFillModeForwards;
    
        NSMutableArray *values = [NSMutableArray array];
        NSMutableArray *timings = [NSMutableArray array];
        NSMutableArray *keytimes = [NSMutableArray array];
    
        //Start
        [values addObject:[NSNumber numberWithFloat:25.0]];
        [timings addObject:GetTiming(kCAMediaTimingFunctionEaseIn)];
        [keytimes addObject:[NSNumber numberWithFloat:0.0]];
    
    
        //Drop down
        [values addObject:[NSNumber numberWithFloat:y]];
        [timings addObject:GetTiming(kCAMediaTimingFunctionEaseOut)];
        [keytimes addObject:[NSNumber numberWithFloat:0.6]];
    
    
        // bounce up
        [values addObject:[NSNumber numberWithFloat:0.7 * y]];
        [timings addObject:GetTiming(kCAMediaTimingFunctionEaseIn)];
        [keytimes addObject:[NSNumber numberWithFloat:0.8]];
    
    
        // fihish down
        [values addObject:[NSNumber numberWithFloat:y]];
        [keytimes addObject:[NSNumber numberWithFloat:1.0]];
        //[timings addObject:GetTiming(kCAMediaTimingFunctionEaseIn)];
    
    
    
        animation.values = values;
        animation.timingFunctions = timings;
        animation.keyTimes = keytimes;
    
        [v.layer addAnimation:animation forKey:nil];   
    
        //[CATransaction commit];
    
    }
    
    链接地址: http://www.djcxy.com/p/39487.html

    上一篇: How to create custom easing function with Core Animation?

    下一篇: Animation: Performance tuning