重要なポイント
Easing(スムージング関数)は、線形時間t ∈ [0,1]を非線形のアニメーション値にマッピングする数学関数f(t)です。アニメーションが500ミリ秒でオブジェクトの位置をAからBに変更する場合、イージングは各時点でオブジェクトがこれらの点間をどのように移動するかを決定します。線形イージング(f(t)=t)は一定速度を意味し、オブジェクトは機械的に動きます。非線形イージングは自然に感じられます。オブジェクトは加速(ease-in)、減速(ease-out)、またはスムーズな開始と終了(ease-in-out)を行います。
イージング関数は3次ベジェ曲線で記述され、X軸は正規化された時間、Y軸は正規化された進行度です。2つの制御点(P1、P2)が曲線の形状を定義します。標準曲線はCSS仕様で固定されています:ease(0.25、0.1、0.25、1.0)、ease-in(0.42、0.0、1.0、1.0)、ease-out(0.0、0.0、0.58、1.0)、ease-in-out(0.42、0.0、0.58、1.0)。これらの同じ値がiOSのCAMediaTimingFunctionとAndroidのPathInterpolatorで使用されています。Material Design Motion Guidelinesによると、イージングカーブはコンテキストに適合する必要があります。出現にはease-out(高速開始)、消失にはease-in(高速終了)です。
iOSでは、イージングは主にCore AnimationのCAMediaTimingFunctionクラスを通じて機能します。これは3次ベジェ曲線を2つの制御点(c1x、c1y、c2x、c2y)として受け入れ、CAAnimation(CABasicAnimation、CAKeyframeAnimation)に適用されます。CAMediaTimingFunctionは5つの定義済み関数をサポートしています:linear、easeIn、easeOut、easeInEaseOut、default(easeInEaseOutと同等)。
CAMediaTimingFunctionはCALayerレベルで動作し、アニメーションは別のレンダリングサーバープロセスで実行されるため、複雑な変換でも60fpsを保証します。UIViewプロパティ(frame、alpha、transform)のアニメーションには、UIKitはoptionsパラメータ.curveEaseInOutを持つUIView.animateを使用し、内部で対応する曲線のCAMediaTimingFunctionも作成します。iOS 17以降では、カスタムベジェ用のUIViewAnimationCurve.customとBlockParametersが追加され、カスタムCAAnimationを作成する必要がなくなりました。CAMediaTimingFunctionはUIKit DynamicsやUIScrollViewの減速(UIScrollViewDecelerationRate)にも使用されています。
SwiftUIでは、イージングはAnimation構造体を通じて.easeIn、.easeOut、.easeInOut、.spring、.interpolatingSpringモディファイアで設定されます。アニメーションが手続き的(beginAnimations/commitAnimations)なUIKitとは異なり、SwiftUIは宣言的アプローチを採用しています。アニメーションは変更される状態プロパティであり、フレームワークが遷移のアニメーション方法を決定します。
.timingCurve(c0x、c0y、c1x、c1y)モディファイアは3次ベジェ制御点を直接受け入れ、CAMediaTimingFunctionの完全な類似物です。SwiftUI Animationは定義済み曲線を静的プロパティとしてサポートしています。Animation.easeInOutはease-in-out(0.42、0.0、0.58、1.0)と同等です。スプリングアニメーションにはAnimation.spring(response:dampingFraction:blendDuration)が使用され、dampingFraction < 1で減衰付きのバネ効果を生み出します。WWDC 2023でAppleはAnimation.keyframesを発表しました。これは各キーフレームでカスタムイージングを使用したフレーム単位のアニメーションで、軌道を完全に制御できます。
Androidでは、イージングはPathInterpolator(API 21+)を通じて実装されています。これは3次ベジェ曲線を持つPathオブジェクトを受け入れるクラスです。制御点はPath.cubicTo(x1、y1、x2、y2、x3、y3)で指定され、(x1、y1)が最初の制御点、(x2、y2)が2番目、(x3、y3)が終了点(常に1、1)です。カスタム曲線が必要な場合はPathInterpolatorが必須です。標準的なシナリオでは、AccelerateDecelerateInterpolator(ease-in-out)、AccelerateInterpolator(ease-in)、DecelerateInterpolator(ease-out)が利用可能です。
Jetpack Composeでは、イージングはandroidx.compose.animationパッケージのEasingオブジェクトを通じて設定されます。FastOutSlowInEasing、LinearOutSlowInEasing、FastOutLinearInEasing、およびCubicBezierEasingによるカスタムEasingが利用可能です。Compose Easingはanimate*AsState、AnimatedVisibility、AnimatedContentで適用されます。Viewシステムとの主な違いは、ComposeはKotlin側でイージングを補間する(RenderNodeではない)ことで、カスタムアニメーションの柔軟性が高まりますが、120Hzディスプレイでの最適化が必要です。
カスタムease-in-out曲線を使用した基本的なレイヤー位置アニメーション。CAMediaTimingFunctionは、動きのスムーズな開始と終了を定義する3次ベジェ制御点を受け入れます。
import UIKit
func animateLayerPosition() {
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = CGPoint(x: 0, y: 0)
animation.toValue = CGPoint(x: 300, y: 400)
animation.duration = 0.8
let easing = CAMediaTimingFunction(
controlPoints: 0.42, 0.0, 0.58, 1.0
)
animation.timingFunction = easing
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
layer.add(animation, forKey: "positionAnimation")
}
制御点(0.42、0.0、0.58、1.0)は標準のease-in-out曲線を定義します。SwiftUIはAnimation.easeInOutに同じ点を使用します。ease-inには(0.42、0.0、1.0、1.0)を、ease-outには(0.0、0.0、0.58、1.0)を使用します。Core Animationはレンダリングサーバーで60/120fpsでアニメーションを実行します。
カスタム曲線とスプリング効果を使用したSwiftUIの宣言的アニメーション。アニケーションモディファイアはisAnimatedの変更にバインドされます。
import SwiftUI
struct EasingExampleView: View {
@State private var isAnimated = false
var body: some View {
VStack {
Circle()
.fill(Color.blue)
.frame(width: 60, height: 60)
.offset(x: isAnimated ? 100 : 0, y: 0)
.animation(
.timingCurve(0.42, 0.0, 0.58, 1.0, duration: 0.6),
value: isAnimated
)
Button("Toggle") {
isAnimated.toggle()
}
}
}
}
.timingCurve(c0x、c0y、c1x、c1y、duration)モディファイアは、SwiftUIでカスタムイージングアニメーションを作成します。valueパラメータはアニメーションを特定の状態にバインドします。isAnimatedが変更されると、SwiftUIが自動的に曲線を適用します。スプリングアニメーションには、timingCurveの代わりに.spring(response:0.4、dampingFraction:0.6)を使用します。
PathInterpolatorによるAndroidでのease-in-out相当。制御点(0.42、0.0、0.58、1.0)はiOSと同じ曲線を定義します。
import android.animation.ObjectAnimator
import android.graphics.Path
import android.view.animation.PathInterpolator
import android.view.View
fun View.fadeInWithEase() {
val path = Path().apply {
cubicTo(0.42f, 0.0f, 0.58f, 1.0f, 1.0f, 1.0f)
}
val interpolator = PathInterpolator(path)
ObjectAnimator.ofFloat(this, "alpha", 0f, 1f).apply {
duration = 500
interpolator = interpolator
start()
}
}
制御点(0.42、0.0、0.58、1.0)を持つPathInterpolatorは、iOSのCAMediaTimingFunction ease-in-outと同じです。クロスプラットフォームプロジェクトでは、同じ制御点を使用することで、視覚的に同一のアニメーション動作が保証されます。Material Designは、Android固有のインターフェースの代替としてFastOutSlowInInterpolator(0.4、0.0、0.2、1.0)を推奨しています。
Composeでは、同じ制御点を持つCubicBezierEasingを通じてイージングが設定されます。結果は、透明度とオフセットを伴うスムーズな要素の出現です。
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
@Composable
fun EasingComposable() {
var visible by remember { mutableStateOf(false) }
val easing = CubicBezierEasing(0.42f, 0.0f, 0.58f, 1.0f)
val alpha by animateFloatAsState(
targetValue = if (visible) 1f else 0f,
animationSpec = tween(durationMillis = 500, easing = easing)
)
Column {
Button(onClick = { visible = !visible }) {
Text("Toggle")
}
Box(
modifier = Modifier
.size(100.dp)
.graphicsLayer(alpha = alpha)
.background(MaterialTheme.colorScheme.primary)
)
}
}
ComposeのCubicBezierEasingは同じ4つの制御点パラメータを受け入れます。animateFloatAsStateは指定された曲線でalpha値を自動的に補間します。比較すると、ComposeのFastOutSlowInEasingは(0.4、0.0、0.2、1.0)と同等で、標準のMaterial Design 3曲線です。CubicBezierEasingによるカスタム曲線は、ブランドアニメーションを完全に制御できます。
よくある質問
EasingはiOS(CAMediaTimingFunction)、Web(CSS ease-in-out)、デザインで使用されるスムージング関数の一般的な概念です。InterpolatorはAndroid(TimeInterpolator)におけるこの概念の実装です。本質的には同じものであり、アニメーション速度の数学的曲線です。違いは名前とプラットフォームAPIのみです。クロスプラットフォームプロジェクトでは、easingという用語が汎用的に使用されます。
iOSは5つの定義済み関数(linear、easeIn、easeOut、easeInEaseOut、custom(3次ベジェ))を持つCAMediaTimingFunctionを提供します。SwiftUIでは、Animation.easeIn、Animation.easeOut、Animation.easeInOut、Animation.spring、Animation.interpolatingSpringが利用可能です。カスタム曲線には、Animation.timingCurve(c0x、c0y、c1x、c1y、duration:)を使用します。
はい、同じ3次ベジェ曲線を設定すれば可能です。iOSのCAMediaTimingFunctionとAndroidのPathInterpolatorは3次ベジェ制御点(x1、y1、x2、y2)を受け入れます。例えば、ease-in-out(0.42、0.0、0.58、1.0)曲線は両方のプラットフォームで同一の動作を提供します。Material Designはクロスプラットフォームアニメーションにこのアプローチを使用しています。
Material Design Motion Guidelinesを使用します。要素の出現にはease-out(高速開始、スムーズな終了)、消失にはease-in(高速終了)、画面間の移動にはease-in-outを使用します。インタラクティブな要素(ボタン、カード)にはスプリングアニメーションを使用します。linearは不自然に見えるため避けてください。
カクつき(ジャンク)は、アニメーションが60fpsに達しない場合に発生します。確認してください:iOSでCAMediaTimingFunction(タイマーではない)を使用していますか?AndroidでPathInterpolator(Handler.postではない)を使用していますか?メインスレッドでの重い計算、複雑なレイヤー階層、大きなdrawableがフレーム落ちの原因になります。iOSでは、複雑なレイヤーにはshouldRasterizeを使用したCore Animationを、AndroidではハードウェアアクセラレーションとGPUレンダリングを使用します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。