IOSでCALayer
(QuadCurve)に沿ってCGPath
を非常にうまくアニメーション化しています。しかし、いくつかの 提供 by Apple(EaseIn/EaseOutなど)。たとえば、バウンスまたはエラスティック関数)よりも興味深いイージング関数を使用したいと思います。
MediaTimingFunction(ベジェ)を使用すると、次のことが可能になります。
しかし、より複雑なタイミング関数を作成したいと思います。問題は、メディアのタイミングには、これらの効果を作成するのに十分に強力ではないキュービックベジェが必要と思われることです:
(ソース: sparrow-framework.org )
上記を作成するための code は、他のフレームワークでは非常に単純であり、非常にイライラさせられます。曲線は、時間位置曲線ではなく、入力時間を出力時間にマッピングしていることに注意してください(T-t曲線)。たとえば、easeOutBounce(T)= tは新しいtを返します。次に、そのtを使用して、動き(またはアニメーション化する必要のあるプロパティ)をプロットします。
だから、複雑なカスタムCAMediaTimingFunction
を作成したいのですが、どうすればよいのか、それが可能かどうかわからないのですか?代替手段はありますか?
編集:
手順の具体例を次に示します。非常に教育的:)
ポイントaからbまでの線に沿ってオブジェクトをアニメートしたいのですが、上のeaseOutBounce曲線を使用して、線に沿った動きを「バウンス」させたいです。つまり、aからbまでの正確な行に従いますが、現在のベジェベースのCAMediaTimingFunctionを使用した場合よりも複雑な方法で加速および減速します。
CGPathで指定された任意の曲線の動きをそのラインにします。曲線に沿って移動する必要がありますが、線の例と同じように加速および減速する必要があります。
理論的には、次のように動作するはずです。
動き曲線をキーフレームアニメーションとして記述してみましょうmove(t)= p、ここでtは時間[0..1]、pは時間で計算された位置t。したがって、move(0)は曲線の開始位置を返し、move(0.5)は正確な中間位置を、move(1)は終了位置を返します。タイミング関数time(T)= tを使用してtの値をmoveに指定すると、必要なものが得られます。バウンス効果の場合、タイミング関数はtime(0.8)とtime(0.8)に同じt値を返す必要があります(単なる例) 。タイミング関数を置き換えるだけで、異なる効果が得られます。
(はい、前後に移動する4つのラインセグメントを作成して結合することでラインバウンスを行うことができますが、それは必要ではありません。結局、それはtimeをマッピングする単純な線形関数です。位置への値。)
ここで理にかなっていることを願っています。
私はこれを見つけました:
愛とココア-コアアニメーションのパラメトリックアクセラレーションカーブ
しかし、ブロックを使用することで、少しシンプルで読みやすくすることができると思います。そのため、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から、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 開発者が操作できる減衰比、mass、stiffness、およびinitial velocityを使用して、目的のスプリング動作を作成します。
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)
継続時間パラメータは引き続きアニメーターに表示されますが、春のタイミングでは無視されます。
これらの2つのオプションが十分でない場合は、UITimingCurveProviderプロトコルを確認することにより、独自のタイミングカーブを実装することもできます。
詳細、異なるタイミングパラメータを使用したアニメーションの作成方法については、 ドキュメント をご覧ください。
また、WWDC 2016の IKitのアニメーションと遷移のプレゼンテーションの進歩 を参照してください。
カスタムタイミング関数を作成する方法は、CAMediaTimingFunctionにfunctionWithControlPoints ::::ファクトリメソッドを使用することです(対応するinitWithControlPoints :::: initがあります)メソッドも)。これは、タイミング関数のベジェ曲線を作成します。任意の曲線ではありませんが、ベジェ曲線は非常に強力で柔軟です。コントロールポイントを理解するには、少し練習が必要です。ヒント:ほとんどの描画プログラムはベジエ曲線を作成できます。それらを操作すると、コントロールポイントで表現している曲線に関する視覚的なフィードバックが得られます。
このリンク はAppleのドキュメントを指します。ビルド機能が曲線からどのように構築されるかについての短いが有用なセクションがあります。
編集:次のコードは、単純なバウンスアニメーションを示しています。そのために、合成タイミング関数(valuesおよびtimingを作成しましたNSArrayプロパティ)、アニメーションの各セグメントに異なる時間長を与えました(keytimesプロパティ)。このようにして、ベジェ曲線を作成して、アニメーションのより洗練されたタイミングを作成できます。 This は、ニースのサンプルコードを含むこのタイプのアニメーションに関する優れた記事です。
- (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];
}
まだ見ているかどうかはわかりませんが、 PRTween は、Core Animationがすぐに使用できる機能、特にカスタムタイミング関数を超える機能を備えている点で、かなり印象的です。また、さまざまなWebフレームワークが提供する一般的なイージングカーブのすべてではないにしても、多くがパッケージ化されています。
私はJesse Crossenの回答を受け取り、少し詳しく説明しました。これを使用して、CGPointsやCGSizeなどをアニメーション化できます。 iOS 7では、UIViewアニメーションで任意の時間関数を使用することもできます。
結果は https://github.com/jjackson26/JMJParametricAnimation で確認できます。
A Swiftバージョンの実装は TFAnimation です。デモはsin曲線アニメーションです。 TFBasicAnimation
は、CABasicAnimation
以外のブロックにtimeFunction
を割り当てることを除いて、timingFunction
と同じです。
キーポイントはサブクラスCAKeyframeAnimation
で、1 / 60fps
s intervalのtimeFunction
によってフレームの位置を計算します。すべての計算値をvalues
of CAKeyframeAnimation
およびkeyTimes
までの間隔で数回。
複数のアニメーションでアニメーショングループを生成するブロックベースのアプローチを作成しました。
各アニメーションは、プロパティごとに、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)