Wichtige Erkenntnisse
Easing (Glättungsfunktion) ist eine mathematische Funktion f(t), die die lineare Zeit t ∈ [0,1] auf einen nichtlinearen Animationswert abbildet. Wenn eine Animation die Position eines Objekts in 500 ms von A nach B ändert, bestimmt Easing, wie sich das Objekt zu jedem Zeitpunkt zwischen diesen Punkten bewegt. Lineares Easing (f(t) = t) bedeutet konstante Geschwindigkeit — das Objekt bewegt sich mechanisch. Nichtlineares Easing fühlt sich natürlich an: Das Objekt beschleunigt (ease-in), verlangsamt (ease-out) oder hat einen sanften Start und ein sanftes Ende (ease-in-out).
Easing-Funktionen werden durch kubische Bézier-Kurven beschrieben, wobei die X-Achse die normalisierte Zeit und die Y-Achse der normalisierte Fortschritt ist. Zwei Kontrollpunkte (P1, P2) definieren die Kurvenform. Die Standardkurven sind durch die CSS-Spezifikation festgelegt: 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). Dieselben Werte werden in CAMediaTimingFunction unter iOS und PathInterpolator unter Android verwendet. Laut den Material Design Motion Guidelines sollten Easing-Kurven dem Kontext entsprechen: für Erscheinen — ease-out (schneller Start), für Verschwinden — ease-in (schnelles Ende).
Unter iOS erfolgt Easing hauptsächlich über die Klasse CAMediaTimingFunction aus Core Animation. Sie akzeptiert eine kubische Bézier-Kurve als zwei Kontrollpunkte (c1x, c1y, c2x, c2y) und wird auf CAAnimation (CABasicAnimation, CAKeyframeAnimation) angewendet. CAMediaTimingFunction unterstützt 5 vordefinierte Funktionen: linear, easeIn, easeOut, easeInEaseOut und default (äquivalent zu easeInEaseOut).
CAMediaTimingFunction arbeitet auf CALayer-Ebene — die Animation wird in einem separaten Render-Server-Prozess ausgeführt, was 60fps auch bei komplexen Transformationen garantiert. Für die Animation von UIView-Eigenschaften (frame, alpha, transform) verwendet UIKit UIView.animate mit dem Options-Parameter: .curveEaseInOut, der intern ebenfalls eine CAMediaTimingFunction mit der entsprechenden Kurve erstellt. Unter iOS 17+ wurde UIViewAnimationCurve.custom mit BlockParameters für benutzerdefinierte Bézier hinzugefügt — dies macht die Erstellung eigener CAAnimation überflüssig. CAMediaTimingFunction wird auch in UIKit Dynamics und UIScrollView zur Verlangsamung (UIScrollViewDecelerationRate) verwendet.
In SwiftUI wird Easing über die Animation-Struktur mit den Modifikatoren .easeIn, .easeOut, .easeInOut, .spring und .interpolatingSpring festgelegt. Im Gegensatz zu UIKit, wo Animation prozedural ist (beginAnimations/commitAnimations), verwendet SwiftUI einen deklarativen Ansatz: Animation ist eine Zustandseigenschaft, die sich ändert, und das Framework bestimmt selbst, wie der Übergang animiert wird.
Der Modifikator .timingCurve(c0x, c0y, c1x, c1y) akzeptiert kubische Bézier-Kontrollpunkte direkt — dies ist ein vollständiges Analogon von CAMediaTimingFunction. SwiftUI Animation unterstützt vordefinierte Kurven als statische Eigenschaften: Animation.easeInOut entspricht ease-in-out (0.42, 0.0, 0.58, 1.0). Für Spring-Animationen wird Animation.spring(response:dampingFraction:blendDuration) verwendet, wobei dampingFraction < 1 einen Federeffekt mit Dämpfung erzeugt. Auf der WWDC 2023 stellte Apple Animation.keyframes vor — Einzelbildanimation mit benutzerdefiniertem Easing auf jedem Schlüsselbild, was volle Kontrolle über die Bewegungsbahn ermöglicht.
Unter Android wird Easing durch PathInterpolator (API 21+) implementiert — eine Klasse, die ein Path-Objekt mit einer kubischen Bézier-Kurve akzeptiert. Kontrollpunkte werden über Path.cubicTo(x1, y1, x2, y2, x3, y3) angegeben, wobei (x1,y1) der erste Kontrollpunkt, (x2,y2) der zweite und (x3,y3) der Endpunkt ist (immer 1,1). PathInterpolator ist erforderlich, wenn eine benutzerdefinierte Kurve benötigt wird; für Standardszenarien stehen AccelerateDecelerateInterpolator (ease-in-out), AccelerateInterpolator (ease-in) und DecelerateInterpolator (ease-out) zur Verfügung.
In Jetpack Compose wird Easing über das Easing-Objekt aus dem Paket androidx.compose.animation festgelegt. FastOutSlowInEasing, LinearOutSlowInEasing, FastOutLinearInEasing sowie benutzerdefiniertes Easing über CubicBezierEasing sind verfügbar. Compose Easing wird in animate*AsState, AnimatedVisibility und AnimatedContent angewendet. Der Hauptunterschied zum View-System: Compose interpoliert Easing auf der Kotlin-Seite (nicht in RenderNode), was mehr Flexibilität für benutzerdefinierte Animationen bietet, aber eine Optimierung bei 120Hz-Displays erfordert.
Basis-Layer-Positionsanimation mit einer benutzerdefinierten Ease-in-Out-Kurve. CAMediaTimingFunction akzeptiert kubische Bézier-Kontrollpunkte, die einen sanften Start und ein sanftes Ende der Bewegung definieren.
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")
}
Die Kontrollpunkte (0.42, 0.0, 0.58, 1.0) definieren die Standard-Ease-in-Out-Kurve. SwiftUI verwendet dieselben Punkte für Animation.easeInOut. Für Ease-in verwenden Sie (0.42, 0.0, 1.0, 1.0); für Ease-out — (0.0, 0.0, 0.58, 1.0). Core Animation führt die Animation im Render-Server mit 60/120 fps aus.
Deklarative Animation in SwiftUI mit einer benutzerdefinierten Kurve und Spring-Effekt. Der Animationsmodifikator bindet an die Änderung von 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()
}
}
}
}
Der Modifikator .timingCurve(c0x, c0y, c1x, c1y, duration) erstellt eine benutzerdefinierte Easing-Animation in SwiftUI. Der Parameter value bindet die Animation an einen bestimmten Zustand — wenn sich isAnimated ändert, wendet SwiftUI automatisch die Kurve an. Für Spring-Animationen verwenden Sie .spring(response: 0.4, dampingFraction: 0.6) anstelle von timingCurve.
Das Ease-in-Out-Äquivalent unter Android über PathInterpolator. Die Kontrollpunkte (0.42, 0.0, 0.58, 1.0) definieren dieselbe Kurve wie unter 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()
}
}
PathInterpolator mit Kontrollpunkten (0.42, 0.0, 0.58, 1.0) ist identisch mit CAMediaTimingFunction ease-in-out unter iOS. Für plattformübergreifende Projekte verwenden Sie dieselben Kontrollpunkte — dies garantiert ein visuell identisches Animationsverhalten. Material Design empfiehlt FastOutSlowInInterpolator (0.4, 0.0, 0.2, 1.0) als Alternative für Android-spezifische Oberflächen.
In Compose wird Easing über CubicBezierEasing mit denselben Kontrollpunkten festgelegt. Das Ergebnis ist ein sanftes Erscheinen des Elements mit Transparenz und Versatz.
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)
)
}
}
CubicBezierEasing in Compose akzeptiert dieselben 4 Kontrollpunktparameter. animateFloatAsState interpoliert automatisch den Alpha-Wert mit der angegebenen Kurve. Zum Vergleich: FastOutSlowInEasing in Compose entspricht (0.4, 0.0, 0.2, 1.0) — der Standardkurve von Material Design 3. Benutzerdefinierte Kurven über CubicBezierEasing geben volle Kontrolle für Markenanimationen.
Häufig gestellte Fragen
Easing ist ein allgemeines Konzept einer Glättungsfunktion, das in iOS (CAMediaTimingFunction), im Web (CSS ease-in-out) und im Design verwendet wird. Interpolator ist die Implementierung dieses Konzepts in Android (TimeInterpolator). Im Wesentlichen sind sie dasselbe — eine mathematische Kurve der Animationsgeschwindigkeit. Der Unterschied liegt nur im Namen und der Plattform-API. In plattformübergreifenden Projekten wird der Begriff Easing als universell verwendet.
iOS bietet CAMediaTimingFunction mit 5 vordefinierten Funktionen: linear, easeIn, easeOut, easeInEaseOut und custom (kubische Bézier). In SwiftUI stehen Animation.easeIn, Animation.easeOut, Animation.easeInOut, Animation.spring und Animation.interpolatingSpring zur Verfügung. Für benutzerdefinierte Kurven verwenden Sie Animation.timingCurve(c0x, c0y, c1x, c1y, duration:).
Ja, wenn dieselbe kubische Bézier-Kurve festgelegt wird. CAMediaTimingFunction unter iOS und PathInterpolator unter Android akzeptieren kubische Bézier-Kontrollpunkte (x1, y1, x2, y2). Zum Beispiel wird die Ease-in-Out-Kurve (0.42, 0.0, 0.58, 1.0) auf beiden Plattformen identisches Verhalten zeigen. Material Design verwendet diesen Ansatz für plattformübergreifende Animationen.
Verwenden Sie die Material Design Motion Guidelines: für das Erscheinen von Elementen — ease-out (schneller Start, sanftes Ende), für das Verschwinden — ease-in (schnelles Ende), für die Bewegung zwischen Bildschirmen — ease-in-out. Verwenden Sie Spring-Animationen für interaktive Elemente (Schaltflächen, Karten). Vermeiden Sie linear — es wirkt unnatürlich.
Ruckeln (Jank) tritt auf, wenn die Animation keine 60fps erreicht. Prüfen Sie: Verwenden Sie CAMediaTimingFunction (keinen Timer) unter iOS, PathInterpolator (nicht Handler.post) unter Android. Schwere Berechnungen im Hauptthread, eine komplexe Layer-Hierarchie oder ein großes Drawable können Frame-Drops verursachen. Verwenden Sie unter iOS Core Animation mit shouldRasterize für komplexe Layer; unter Android — Hardware Acceleration und GPU-Rendering.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch