Hlavní body
Easing (funkce zjemnění) — matematická funkce f(t), která mapuje lineární průběh času t ∈ [0,1] na nelineární hodnotu animace. Pokud animace mění polohu objektu z A na B za 500 ms, easing určuje, jak se objekt pohybuje mezi těmito body v každém okamžiku. Lineární easing (f(t) = t) znamená konstantní rychlost — objekt se pohybuje mechanicky. Nelineární easing — přirozeně: objekt zrychluje (ease-in), zpomaluje (ease-out) nebo má hladký začátek a konec (ease-in-out).
Funkce easingu jsou popsány kubickými Bézier křivkami (Cubic Bézier), kde osa X je normalizovaný čas a osa Y je normalizovaný pokrok. Dva řídicí body (P1, P2) určují tvar křivky. Standardní křivky jsou stanoveny specifikací 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). Stejné hodnoty se používají v CAMediaTimingFunction v iOS a PathInterpolator v Androidu. Podle pokynů Material Design Motion by křivky easingu měly odpovídat kontextu: pro zobrazení — ease-out (rychlý start), pro zmizení — ease-in (rychlý konec).
V iOS probíhá hlavní práce s easingem prostřednictvím třídy CAMediaTimingFunction z Core Animation. Ta přijímá kubickou Bézier křivku ve formě dvou řídicích bodů (c1x, c1y, c2x, c2y) a aplikuje se na CAAnimation (CABasicAnimation, CAKeyframeAnimation). CAMediaTimingFunction podporuje 5 předdefinovaných funkcí: linear, easeIn, easeOut, easeInEaseOut a default (ekvivalent easeInEaseOut).
CAMediaTimingFunction pracuje na úrovni CALayer — animace je prováděna v samostatném procesu render server, což zaručuje 60fps i při složitých transformacích. Pro animaci vlastností UIView (frame, alpha, transform) UIKit používá UIView.animate s parametrem options: .curveEaseInOut, který interně také vytváří CAMediaTimingFunction s odpovídající křivkou. V iOS 17+ byl přidán UIViewAnimationCurve.custom s BlockParameters pro vlastní Bézier — to eliminuje potřebu psát vlastní CAAnimation. CAMediaTimingFunction se také používá v UIKit Dynamics a UIScrollView pro deceleraci (UIScrollViewDecelerationRate).
Ve SwiftUI je easing definován prostřednictvím struktury Animation s modifikátory .easeIn, .easeOut, .easeInOut, .spring a .interpolatingSpring. Na rozdíl od UIKit, kde je animace procedurální (beginAnimations/commitAnimations), SwiftUI používá deklarativní přístup: animace je vlastnost stavu, která se mění, a framework sám určuje, jak animovat přechod.
Modifikátor .timingCurve(c0x, c0y, c1x, c1y) přijímá přímo řídicí body kubické Bézier — to je úplná analogie CAMediaTimingFunction. SwiftUI Animation podporuje předdefinované křivky jako statické vlastnosti: Animation.easeInOut je ekvivalentní ease-in-out (0.42, 0.0, 0.58, 1.0). Pro pružinovou animaci se používá Animation.spring(response:dampingFraction:blendDuration), kde dampingFraction < 1 vytváří efekt pružiny s tlumením. Na WWDC 2023 Apple představil Animation.keyframes — snímkovou animaci s vlastním easingem na každém klíčovém snímku, což poskytuje úplnou kontrolu nad trajektorií.
V Androidu je easing implementován prostřednictvím PathInterpolator (API 21+) — třídy, která přijímá objekt Path s kubickou Bézier křivkou. Řídicí body jsou definovány pomocí Path.cubicTo(x1, y1, x2, y2, x3, y3), kde (x1,y1) je první řídicí bod, (x2,y2) druhý, (x3,y3) koncový bod (vždy 1,1). PathInterpolator je povinný, pokud je potřeba vlastní křivka; pro standardní scénáře jsou k dispozici AccelerateDecelerateInterpolator (ease-in-out), AccelerateInterpolator (ease-in) a DecelerateInterpolator (ease-out).
V Jetpack Compose je easing definován prostřednictvím objektu Easing z balíčku androidx.compose.animation. K dispozici jsou FastOutSlowInEasing, LinearOutSlowInEasing, FastOutLinearInEasing, stejně jako vlastní Easing přes CubicBezierEasing. Compose Easing se aplikuje v animate*AsState, AnimatedVisibility a AnimatedContent. Klíčový rozdíl od systému View: Compose interpoluje easing na straně Kotlin (ne v RenderNode), což poskytuje větší flexibilitu pro vlastní animace, ale vyžaduje optimalizaci na 120Hz displejích.
Základní animace pozice vrstvy s vlastní křivkou ease-in-out. CAMediaTimingFunction přijímá řídicí body kubické Bézier definující hladký začátek a konec pohybu.
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")
}
Řídicí body (0.42, 0.0, 0.58, 1.0) definují standardní křivku ease-in-out. SwiftUI používá stejné body pro Animation.easeInOut. Pro ease-in použijte (0.42, 0.0, 1.0, 1.0), pro ease-out — (0.0, 0.0, 0.58, 1.0). Core Animation provádí animaci v render server s frekvencí 60/120 fps.
Deklarativní animace ve SwiftUI s vlastní křivkou a pružinovým efektem. Modifikátor animation je navázán na změnu 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()
}
}
}
}
Modifikátor .timingCurve(c0x, c0y, c1x, c1y, duration) vytváří vlastní easing animaci ve SwiftUI. Parametr value připojuje animaci ke konkrétnímu stavu — při změně isAnimated SwiftUI automaticky aplikuje křivku. Pro pružinovou animaci použijte .spring(response: 0.4, dampingFraction: 0.6) místo timingCurve.
Ekvivalent ease-in-out v Androidu přes PathInterpolator. Řídicí body (0.42, 0.0, 0.58, 1.0) definují stejnou křivku jako v 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 s řídicími body (0.42, 0.0, 0.58, 1.0) je identický s CAMediaTimingFunction ease-in-out na iOS. Pro multiplatformní projekty používejte stejné řídicí body — to zaručuje vizuálně identické chování animace. Material Design doporučuje FastOutSlowInInterpolator (0.4, 0.0, 0.2, 1.0) jako alternativu pro Android-specifická rozhraní.
V Compose je easing definován pomocí CubicBezierEasing se stejnými řídicími body. Výsledek — plynulé zobrazení prvku s průhledností a posunem.
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 v Compose přijímá stejné 4 parametry řídicích bodů. animateFloatAsState automaticky interpoluje hodnotu alpha s danou křivkou. Pro srovnání: FastOutSlowInEasing v Compose je ekvivalentní (0.4, 0.0, 0.2, 1.0) — standardní křivka Material Design 3. Vlastní křivky přes CubicBezierEasing poskytují úplnou kontrolu pro značkové animace.
Často kladené otázky
Easing — obecný pojem funkce zjemnění, používaný v iOS (CAMediaTimingFunction), webu (CSS ease-in-out) a designu. Interpolator — implementace tohoto konceptu v Androidu (TimeInterpolator). V podstatě je to totéž — matematická křivka rychlosti animace. Rozdíl je pouze v názvu a API platformy. V multiplatformních projektech se termín easing používá jako univerzální.
iOS poskytuje CAMediaTimingFunction s 5 předdefinovanými funkcemi: linear, easeIn, easeOut, easeInEaseOut a custom (kubická Bézier). Ve SwiftUI jsou k dispozici Animation.easeIn, Animation.easeOut, Animation.easeInOut, Animation.spring a Animation.interpolatingSpring. Pro vlastní křivky použijte Animation.timingCurve(c0x, c0y, c1x, c1y, duration:).
Ano, pokud nastavíte stejnou kubickou Bézier křivku. CAMediaTimingFunction v iOS a PathInterpolator v Androidu přijímají řídicí body kubické Bézier (x1, y1, x2, y2). Například křivka ease-in-out (0.42, 0.0, 0.58, 1.0) bude mít stejné chování na obou platformách. Material Design používá tento přístup pro multiplatformní animace.
Použijte pokyny Material Design Motion: pro zobrazování prvků — ease-out (rychlý start, hladký konec), pro mizení — ease-in (rychlý konec), pro přesun mezi obrazovkami — ease-in-out. Použijte pružinovou animaci pro interaktivní prvky (tlačítka, karty). Vyhněte se linear — vypadá nepřirozeně.
Trhání (jank) nastává, když animace nedosahuje 60fps. Zkontrolujte: používáte CAMediaTimingFunction (ne timer) v iOS, PathInterpolator (ne Handler.post) v Androidu. Těžké výpočty v hlavním vlákně, složitá hierarchie vrstev nebo velký drawable mohou způsobovat ztrátu snímků. V iOS použijte Core Animation s shouldRasterize pro složité vrstvy; v Androidu — Hardware Acceleration a GPU rendering.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také