如何使用Core Animation创建自定义缓动功能? [英] How to create custom easing function with Core Animation?

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

问题描述

我正在动画一个 CALayer 沿着 CGPath (QuadCurve)在iOS相当不错。但我想使用比少数更有趣的缓动函数创建上面是足够简单的其他框架,这使这非常令人沮丧。注意,曲线是将输入时间映射到输出时间(T-t曲线),而不是时间 - 位置曲线。例如, easeOutBounce(T)= t 会返回一个新的 t 。然后, t 用于绘制运动(或任何我们应该动画的属性)。



创建一个复杂的自定义 CAMediaTimingFunction 但我没有线索如何做,或者如果它甚至可能?是否有任何替代方案?



编辑



一个具体的例子。非常教育:)


  1. 我想使一个对象沿着从 em> b ,但我希望它使用上面的easeOutBounce曲线沿着线反弹它的运动。这意味着它将遵循从 b 的确切行,但将以比使用当前基于bezier的CAMediaTimingFunction更复杂的方式加速和减速。


  2. 让该行使用CGPath指定的任意曲线运动。


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



让我们将运动曲线描述为一个关键帧动画。move(t)= p em> t 是时间[0..1], p 是在时间 t时计算的位置。因此,move(0)返回曲线开始处的位置,移动(0.5)确切的中间位置,并在结束处 move(1)。使用定时函数 time(T)= t 移动提供 t 值应该给我想要的。对于跳动效应,定时函数应当返回时间(0.8)时间(0.8)的相同的



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



我希望我'

我发现这个:



a href =http://cocoawithlove.com/2008/09/parametric-acceleration-curves-in-core.html> Cocoa with Love - Core Animation中的参数加速曲线



但我认为它可以做一个更简单,更可读的使用块。因此,我们可以在CAKeyframeAnimation上定义一个类别,如下所示:



CAKeyframeAnimation + Parametric.h:

  //这应该是一个函数,它接受
// 0.0和1.0之间的时间值(其中0.0是动画的开头
//,1.0是end)并返回一个比例因子,其中0.0
//将产生起始值,1.0将产生
//结束值
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 {
//获取关键帧动画来设置
CAKeyframeAnimation * animation =
[CAKeyframeAnimation animationWithKeyPath:path];
//将时间分成步骤
//(更多的步骤,更平滑的动画)
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;
}
//我们想要的关键帧之间的线性动画,具有相等的时间步长
animation.calculationMode = kCAAnimationLinear;
//设置关键帧,我们完成了
[animation setValues:values];
return(animation);
}

@end

这个:

  //定义一个参数函数
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];

//制作一个动画
CAAnimation * drop = [CAKeyframeAnimation
animationWithKeyPath:@position.y
function:function fromValue:30.0 toValue:450.0];
//使用它
[layer addAnimation:drop forKey:@position];

[CATransaction commit];
}

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


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创建自定义缓动功能?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆