本文介绍了如何使用 Core Animation 创建自定义缓动功能?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 iOS 中很好地沿着 CGPath (QuadCurve) 为 CALayer 设置动画.但我想使用比少数

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

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

这是步骤的具体示例.很有教育意义:)

  1. 我想沿着从点 ab 的线为对象设置动画,但我希望它使用上面的easeOutBounce曲线.这意味着它将遵循从 ab 的确切路线,但将以比使用当前基于贝塞尔曲线的 CAMediaTimingFunction 可能实现的方式更复杂的方式加速和减速.

  2. 让这条线由CGPath指定的任意曲线移动.它仍应沿该曲线移动,但应以与直线示例中相同的方式加速和减速.

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

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

(是的,可以通过创建和连接四个来回的线段来进行线弹跳,但这不是必需的.毕竟,它只是一个映射时间的简单线性函数 值到位置.)

我希望我在这里说得通.

解决方案

我发现了这个:

Cocoa with Love - Core Animation 中的参数化加速度曲线

但我认为通过使用块可以使它更简单和更具可读性.所以我们可以在 CAKeyframeAnimation 上定义一个看起来像这样的类别:

CAKeyframeAnimation+Parametric.h:

//这应该是一个函数,它需要一个时间值//0.0 和 1.0(其中 0.0 是动画的开始//1.0 是结束)并返回一个比例因子,其中 0.0//将产生起始值,1.0 将产生//结束值typedef double (^KeyframeParametricBlock)(double);@interface CAKeyframeAnimation(参数化)+ (id)animationWithKeyPath:(NSString *)path功能:(KeyframeParametricBlock)块从值:(双)从值toValue:(double)toValue;

CAKeyframeAnimation+Parametric.m:

@implementation CAKeyframeAnimation(参数化)+ (id)animationWithKeyPath:(NSString *)path功能:(KeyframeParametricBlock)块从值:(双)从值toValue:(双)toValue {//获取要设置的关键帧动画CAKeyframeAnimation *动画 =[CAKeyframeAnimation animationWithKeyPath:path];//将时间分解为步骤//(步数越多,动画越流畅)NSUInteger 步数 = 100;NSMutableArray *values = [NSMutableArray arrayWithCapacity:steps];双倍时间 = 0.0;double timeStep = 1.0/(double)(steps - 1);for(NSUInteger i = 0; i 

现在用法看起来像这样:

//定义一个参数函数KeyframeParametricBlock 函数 = ^double(double time) {返回(1.0 - pow((1.0 - 时间),2.0));};如果(层){[CA事务开始];[CA交易setValue:[NSNumber numberWithFloat:2.5]forKey:kCATransactionAnimationDuration];//制作动画CAAnimation *drop = [CAKeyframeAnimationanimationWithKeyPath:@"position.y"函数:函数 fromValue:30.0 toValue:450.0];//用它[layer addAnimation:drop forKey:@"position"];[CATransaction 提交];}

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

I am animating a CALayer along a CGPath (QuadCurve) quite nicely in iOS. But I'd like to use a more interesting easing function than the few provided by Apple (EaseIn/EaseOut etc). For instance, a bounce or elastic function.

These things are possible to do with MediaTimingFunction (bezier):

But I'd like to create timing functions that are more complex. Problem is that media timing seems to require a cubic bezier which is not powerful enough to create these effects:


The code to create the above is simple enough in other frameworks, which makes this very frustrating. Note that the curves are mapping input time to output time (T-t curve) and not time-position curves. For instance, easeOutBounce(T) = t returns a new t. Then that t is used to plot the movement (or whatever property we should animate).

So, I'd like to create a complex custom CAMediaTimingFunction but I have no clue how to do that, or if it's even possible? Are there any alternatives?

EDIT:

Here is a concrete example in to steps. Very educational :)

  1. I want to animate an object along a line from point a to b, but I want it to "bounce" its movement along the line using the easeOutBounce curve above. This means it will follow the exact line from a to b, but will accelerate and decelerate in a more complex way than what is possible using the current bezier-based CAMediaTimingFunction.

  2. Lets make that line any arbitrary curve movement specified with CGPath. It should still move along that curve, but it should accelerate and decelerate the same way as in the line example.

In theory I think it should work like this:

Lets describe the movement curve as a keyframe animation move(t) = p, where t is time [0..1], p is position calculated at time t. So move(0) returns the position at the start of curve, move(0.5) the exact middle and move(1) at end. Using a an timing function time(T) = t to provide the t values for move should give me what I want. For a bouncing effect, the timing function should return the same t values for time(0.8) and time(0.8) (just an example). Just replace the timing function to get a different effect.

(Yes, it's possible to do line-bouncing by creating and joining four line segments which goes back and forth, but that shouldn't be necessary. After all, it's just a simple linear function which maps time values to positions.)

I hope I'm making sense here.

解决方案

I found this:

Cocoa with Love - Parametric acceleration curves in Core Animation

But I think it can be made a little simpler and more readable by using blocks. So we can define a category on CAKeyframeAnimation that looks something like this:

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

Now usage will look something like this:

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

I know it might not be quite as simple as what you wanted, but it's a start.

这篇关于如何使用 Core Animation 创建自定义缓动功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-12 08:32