Core Animationでカスタムイージング関数を作成するにはどうすればよいですか?


115

iOSで(QuadCurve)にCALayer沿ってをCGPathうまくアニメーション化しています。しかし、私はAppleが提供するいくつかよりも面白いイージング関数(EaseIn / EaseOutなど)を使用したいと思います。たとえば、バウンスや弾性関数。

これらはMediaTimingFunction(ベジェ)で実行できます。

ここに画像の説明を入力してください

しかし、もっと複雑なタイミング関数を作成したいと思います。問題は、メディアのタイミングには、これらの効果を作成するのに十分なほど強力ではない3次ベジェが必要であるように見えることです。

ここに画像の説明を入力してください
(ソース:sparrow-framework.org

コード上記を作成するには、この非常にイライラになり、他のフレームワークでは、単純な十分、です。曲線は入力時間を出力時間(Tt曲線)にマッピングしており、時間位置曲線ではないことに注意してください。たとえば、easeOutBounce(T)= tは新しいtを返します。次に、そのtを使用して動き(またはアニメーション化する必要があるプロパティ)をプロットします。

だから、私は複雑なカスタムを作成したいのCAMediaTimingFunctionですが、どうすればよいのか、それが可能かどうかさえわかりません。代替案はありますか?

編集:

手順の具体例を以下に示します。非常に教育的:)

  1. オブジェクトをポイントaからbまでのラインに沿ってアニメートしたいのですが、上のeaseOutBounceカーブを使用して、ラインに沿ってオブジェクトの動きを「バウンス」させます。これは、aからbまでの正確な線をたどりますが、現在のベジェベースのCAMediaTimingFunctionを使用した場合よりも複雑な方法で加速および減速します。

  2. その線をCGPathで指定された任意の曲線の動きにしましょう。それでもその曲線に沿って移動するはずですが、直線の例と同じように加速および減速するはずです。

理論的には、次のように機能するはずです。

移動曲線をキーフレームアニメーションmove(t)= pとして説明します。ここで、tは時間[0..1]であり、pは時間tで計算された位置です。したがって、move(0)はカーブの開始位置、move(0.5)は正確な中央、move(1)は終了位置を返します。タイミング関数time(T)= tを使用して移動t値を提供すると、必要なものが得られます。バウンス効果の場合、タイミング関数はtime(0.8)time(0.8)に同じt値を返す必要があります(単なる例)。タイミング関数を置き換えるだけで、別の効果が得られます。

(はい、前後に移動する4つのラインセグメントを作成して結合することでラインバウンスを行うことは可能ですが、それは必要ありません。結局のところ、時間値を位置にマップする単純な線形関数です。)

ここで意味が通じているといいのですが。


(SOでよくあることですが)この非常に古い質問には非常に古くなった回答が含まれていることに注意してください。(非常に注目に値する:cubic-bezier.com!)
Fattie

回答:


48

私はこれを見つけました:

Cocoa with Love-Core Animationのパラメトリック加速曲線

しかし、私はそれをブロックを使用することによって、もう少し簡単で読みやすくすることができると思います。したがって、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];
}

思ったほど簡単ではないかもしれませんが、それは始まりです。


1
私はジェシー・クロスンの反応を受け取り、少し拡張しました。これを使用して、CGPoints、およびCGSizeなどをアニメーション化できます。iOS 7以降、UIViewアニメーションで任意の時間関数を使用することもできます。結果はgithub.com/jjackson26/JMJParametricAnimation
JJ Jackson

@JJJacksonアニメーションの素晴らしいコレクション!それらを収集してくれてありがとう
Codey

33

iOS 10以降、2つの新しいタイミングオブジェクトを使用して、カスタムタイミング関数を簡単に作成できるようになりました。

1)UICubicTimingParametersでは、3次ベジェ曲線をイージング関数として定義できます。

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に表示されますが、春のタイミングでは無視されます。

これら2つのオプションでは不十分な場合は、UITimingCurveProviderプロトコルを確認することにより、独自のタイミングカーブを実装することもできます。

詳しくは、さまざまなタイミングパラメータを使用してアニメーションを作成する方法についてのドキュメントをご覧ください。

また、WWDC 2016の「UIKitアニメーションと移行の進歩」のプレゼンテーションもご覧ください。



「これら2つのオプションでは不十分な場合は、UITimingCurveProviderプロトコルを確認して独自のタイミングカーブを実装することもできます。」?
クリスチャンシュノール2018年

驚くばかり!()のCoreAnimationバージョンはiOS 9までサポートされています!UISpringTimingParametersCASpringAnimation
Rhythmic Fistman

@ OlgaKonoreva、cubic-bezier.comサービスは非常に価格高く、すばらしいです。あなたがSOユーザーであることに満足していることを願って-感謝を言うためにあなたに太ったお尻の賞金を送ります:)
Fattie

@ChristianSchnorr答えがUITimingCurveProviderわからないのは、立方体またはばねのアニメーションを表すだけでなく、立方体ばねの両方を組み合わせたアニメーションを表す能力でもあると思いますUITimingCurveType.composed
デビッドジェームズ

14

カスタムタイミング関数を作成する方法は、CAMediaTimingFunctionでfunctionWithControlPoints ::::ファクトリメソッドを使用することです(対応するinitWithControlPoints :::: initメソッドもあります)。これにより、タイミング関数のベジエ曲線が作成されます。任意曲線ではありませんが、ベジエ曲線は非常に強力で柔軟です。コントロールポイントのこつを取得するには少し練習が必要です。ヒント:ほとんどの描画プログラムはベジエ曲線を作成できます。それらをいじると、コントロールポイントで表現している曲線について視覚的なフィードバックが得られます。

このリンクは、Appleのドキュメントを指します。事前構築機能が曲線からどのように構築されるかについての短いですが便利なセクションがあります。

編集:次のコードは、単純なバウンスアニメーションを示しています。そのために、合成タイミング関数(タイミング NSArrayプロパティ)を作成し、アニメーションの各セグメントに異なる時間の長さ(keytimesプロパティ)を与えました。このようにして、ベジエ曲線を作成して、アニメーションのより高度なタイミングを作成できます。これは、このタイプのアニメーションに関する優れた記事で、優れたサンプルコードが含まれています。

- (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];

}

はい、そうです。CAKeyframeAnimationのvaluesプロパティとtimingFunctionsプロパティを使用して複数のタイミング関数を組み合わせて、目的の機能を実現できます。例を考えて、コードを少し入れます。
fsaint 2011年

あなたの努力に感謝し、試行するためのポイント:)そのアプローチ理論的にうまくいくかもしれませんが、それぞれの用途に合わせてカスタマイズする必要があります。すべてのアニメーションでタイミング関数として機能する一般的なソリューションを探しています。正直に言うと、線と曲線をつなぎ合わせてもそれほどよく見えません。「ボックスインジャック」の例もこれに悩まされています。
マーティンウィックマン

1
キーフレームアニメーションに値の配列の代わりにCGPathRefを指定できますが、最終的にはFelzは正しいです。CAKeyframeAnimationとtimingFunctionsは必要なすべてを提供します。それが「用途ごとにカスタマイズする」必要がある「一般的な解決策」ではないと思う理由がわかりません。キーフレームアニメーションは、ファクトリーメソッドなどで一度構築すると、そのアニメーションを任意のレイヤーに何度でも追加できます。用途ごとにカスタマイズする必要があるのはなぜですか?タイミング関数がどのように機能するかについてのあなたの考えと同じだと私には思えます。
Matt Long

1
ああ、これらは現在4年遅れですが、functionWithControlPoints ::::が完全に弾力性のある/弾力のあるアニメーションを実行できる場合 cubic-bezier.com/#.6,1.87,.63,.74
Matt Foley

10

まだ探しているかどうかはわかりませんが、PRTweenは、Core Animation が最初から提供している機能、特にカスタムタイミング関数を超えて機能するという点でかなり印象的です。また、すべてではないにしても、さまざまなWebフレームワークが提供する一般的なイージング曲線の多くがパッケージ化されています。


2

迅速なバージョンの実装はTFAnimationです。デモはsinカーブアニメーションです。以外のブロックで割り当てる以外はTFBasicAnimation同じようにCABasicAnimation使用timeFunctiontimingFunctionます。

重要な点はサブクラスでCAKeyframeAnimationありtimeFunction1 / 60fpss間隔でフレーム位置を計算します。結局のところ、計算されたすべての値をvaluesof CAKeyframeAnimationに、時間間隔をtoに追加しkeyTimesます。


2

複数のアニメーションを含むアニメーショングループを生成するブロックベースのアプローチを作成しました。

各アニメーションは、プロパティごとに、33の異なるパラメトリックカーブの1つ、初期速度のディケイタイミング関数、またはニーズに合わせて構成されたカスタムスプリングを使用できます。

グループが生成されると、ビューにキャッシュされ、アニメーションの有無にかかわらず、AnimationKeyを使用してトリガーできます。トリガーされると、アニメーションはプレゼンテーションレイヤーの値に応じて同期され、それに応じて適用されます。

フレームワークはこちらFlightAnimatorにあります

以下に例を示します。

struct AnimationKeys {
    static let StageOneAnimationKey  = "StageOneAnimationKey"
    static let StageTwoAnimationKey  = "StageTwoAnimationKey"
}

...

view.registerAnimation(forKey: AnimationKeys.StageOneAnimationKey, maker:  { (maker) in

    maker.animateBounds(toValue: newBounds,
                     duration: 0.5,
                     easingFunction: .EaseOutCubic)

    maker.animatePosition(toValue: newPosition,
                     duration: 0.5,
                     easingFunction: .EaseOutCubic)

    maker.triggerTimedAnimation(forKey: AnimationKeys.StageTwoAnimationKey,
                           onView: self.secondaryView,
                           atProgress: 0.5, 
                           maker: { (makerStageTwo) in

        makerStageTwo.animateBounds(withDuration: 0.5,
                             easingFunction: .EaseOutCubic,
                             toValue: newSecondaryBounds)

        makerStageTwo.animatePosition(withDuration: 0.5,
                             easingFunction: .EaseOutCubic,
                             toValue: newSecondaryCenter)
     })                    
})

アニメーションをトリガーするには

view.applyAnimation(forKey: AnimationKeys.StageOneAnimationKey)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.