Huvudpunkter
Easing (utjämningsfunktion) — en matematisk funktion f(t) som avbildar tidens linjära förlopp t ∈ [0,1] till ett icke-linjärt animationsvärde. Om en animation ändrar ett objekts position från A till B på 500 ms, bestämmer easing hur objektet rör sig mellan dessa punkter vid varje tidpunkt. Linjär easing (f(t) = t) innebär konstant hastighet — objektet rör sig mekaniskt. Icke-linjär easing — naturligt: objektet accelererar (ease-in), saktar ner (ease-out) eller har en mjuk start och slut (ease-in-out).
Easing-funktioner beskrivs med kubiska Bézier-kurvor (Cubic Bézier), där X-axeln är normaliserad tid och Y-axeln är normaliserat framsteg. Två kontrollpunkter (P1, P2) bestämmer kurvans form. Standardkurvor är fastställda av CSS-specifikationen: 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). Samma värden används i CAMediaTimingFunction i iOS och PathInterpolator i Android. Enligt Material Design Motion-riktlinjerna ska easing-kurvor motsvara sammanhanget: för uppdykande — ease-out (snabb start), för försvinnande — ease-in (snabb finish).
I iOS sker det huvudsakliga arbetet med easing genom klassen CAMediaTimingFunction från Core Animation. Den tar emot en kubisk Bézier-kurva i form av två kontrollpunkter (c1x, c1y, c2x, c2y) och tillämpas på CAAnimation (CABasicAnimation, CAKeyframeAnimation). CAMediaTimingFunction stöder 5 fördefinierade funktioner: linear, easeIn, easeOut, easeInEaseOut och default (motsvarande easeInEaseOut).
CAMediaTimingFunction fungerar på CALayer-nivå — animationen utförs i en separat process, render server, vilket garanterar 60fps även vid komplexa transformationer. För animation av UIView-egenskaper (frame, alpha, transform) använder UIKit UIView.animate med parametern options: .curveEaseInOut, som internt också skapar en CAMediaTimingFunction med motsvarande kurva. I iOS 17+ har UIViewAnimationCurve.custom med BlockParameters för anpassad Bézier lagts till — detta eliminerar behovet av att skriva en egen CAAnimation. CAMediaTimingFunction används även i UIKit Dynamics och UIScrollView för retardation (UIScrollViewDecelerationRate).
I SwiftUI definieras easing genom strukturen Animation med modifierare .easeIn, .easeOut, .easeInOut, .spring och .interpolatingSpring. Till skillnad från UIKit där animation är proceduriell (beginAnimations/commitAnimations) använder SwiftUI ett deklarativt tillvägagångssätt: animation är en tillståndsegenskap som ändras och ramverket bestämmer själv hur övergången ska animeras.
Modifieraren .timingCurve(c0x, c0y, c1x, c1y) tar emot kontrollpunkterna för den kubiska Bézier-kurvan direkt — detta är den fullständiga motsvarigheten till CAMediaTimingFunction. SwiftUI Animation stöder fördefinierade kurvor som statiska egenskaper: Animation.easeInOut motsvarar ease-in-out (0.42, 0.0, 0.58, 1.0). För fjäderanimation används Animation.spring(response:dampingFraction:blendDuration), där dampingFraction < 1 skapar en fjäderverkan med dämpning. På WWDC 2023 introducerade Apple Animation.keyframes — bild-för-bild-animation med anpassad easing på varje nyckelbild, vilket ger fullständig kontroll över banan.
I Android implementeras easing genom PathInterpolator (API 21+) — en klass som tar emot ett Path-objekt med en kubisk Bézier-kurva. Kontrollpunkterna definieras genom Path.cubicTo(x1, y1, x2, y2, x3, y3), där (x1,y1) är första kontrollpunkten, (x2,y2) den andra och (x3,y3) slutpunkten (alltid 1,1). PathInterpolator är obligatorisk om en anpassad kurva behövs; för standardscenarier finns AccelerateDecelerateInterpolator (ease-in-out), AccelerateInterpolator (ease-in) och DecelerateInterpolator (ease-out).
I Jetpack Compose definieras easing genom objektet Easing från paketet androidx.compose.animation. FastOutSlowInEasing, LinearOutSlowInEasing, FastOutLinearInEasing samt anpassad Easing genom CubicBezierEasing finns tillgängliga. Compose Easing tillämpas i animate*AsState, AnimatedVisibility och AnimatedContent. Den viktigaste skillnaden från View-systemet: Compose interpolerar easing på Kotlin-sidan (inte i RenderNode), vilket ger mer flexibilitet för anpassade animationer, men kräver optimering på 120Hz-skärmar.
Grundläggande animation av lagerposition med anpassad ease-in-out-kurva. CAMediaTimingFunction tar emot kontrollpunkterna för den kubiska Bézier-kurvan som definierar en mjuk start och slut på rörelsen.
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")
}
Kontrollpunkterna (0.42, 0.0, 0.58, 1.0) definierar standardkurvan ease-in-out. SwiftUI använder samma punkter för Animation.easeInOut. För ease-in använd (0.42, 0.0, 1.0, 1.0), för ease-out — (0.0, 0.0, 0.58, 1.0). Core Animation utför animationen i render server med en frekvens på 60/120 fps.
Deklarativ animation i SwiftUI med anpassad kurva och fjäderverkan. Modifieraren animation är kopplad till ändringen av 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()
}
}
}
}
Modifieraren .timingCurve(c0x, c0y, c1x, c1y, duration) skapar en anpassad easing-animation i SwiftUI. Parametern value kopplar animationen till ett specifikt tillstånd — när isAnimated ändras tillämpar SwiftUI automatiskt kurvan. För fjäderanimation, använd .spring(response: 0.4, dampingFraction: 0.6) istället för timingCurve.
Motsvarigheten till ease-in-out i Android genom PathInterpolator. Kontrollpunkterna (0.42, 0.0, 0.58, 1.0) definierar samma kurva som i 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 med kontrollpunkter (0.42, 0.0, 0.58, 1.0) är identisk med CAMediaTimingFunction ease-in-out på iOS. För plattformsoberoende projekt, använd samma kontrollpunkter — detta garanterar visuellt identiskt animationsbeteende. Material Design rekommenderar FastOutSlowInInterpolator (0.4, 0.0, 0.2, 1.0) som ett alternativ för Android-specifika gränssnitt.
I Compose definieras easing genom CubicBezierEasing med samma kontrollpunkter. Resultatet — en mjuk uppdykning av ett element med transparens och förskjutning.
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 i Compose tar emot samma 4 parametrar för kontrollpunkter. animateFloatAsState interpolerar automatiskt alfavärdet med den givna kurvan. För jämförelse: FastOutSlowInEasing i Compose motsvarar (0.4, 0.0, 0.2, 1.0) — standardkurvan för Material Design 3. Anpassade kurvor genom CubicBezierEasing ger full kontroll för varumärkesanimationer.
Vanliga frågor
Easing — det allmänna begreppet för utjämningsfunktion, som används i iOS (CAMediaTimingFunction), webben (CSS ease-in-out) och design. Interpolator — implementeringen av detta koncept i Android (TimeInterpolator). I grund och botten är det samma sak — en matematisk kurva för animationshastighet. Skillnaden är bara i namn och plattformens API. I plattformsoberoende projekt används termen easing som universell.
iOS erbjuder CAMediaTimingFunction med 5 fördefinierade funktioner: linear, easeIn, easeOut, easeInEaseOut och custom (kubisk Bézier). I SwiftUI finns Animation.easeIn, Animation.easeOut, Animation.easeInOut, Animation.spring och Animation.interpolatingSpring. För anpassade kurvor, använd Animation.timingCurve(c0x, c0y, c1x, c1y, duration:).
Ja, om du ställer in samma kubiska Bézier-kurva. CAMediaTimingFunction i iOS och PathInterpolator i Android accepterar kontrollpunkterna för kubisk Bézier (x1, y1, x2, y2). Till exempel kommer kurvan ease-in-out (0.42, 0.0, 0.58, 1.0) att ge identiskt beteende på båda plattformarna. Material Design använder detta tillvägagångssätt för plattformsoberoende animationer.
Använd Material Design Motion-riktlinjerna: för uppdykande av element — ease-out (snabb start, mjuk finish), för försvinnande — ease-in (snabb finish), för förflyttning mellan skärmar — ease-in-out. Använd fjäderanimation för interaktiva element (knappar, kort). Undvik linear — det ser onaturligt ut.
Ryckighet (jank) uppstår när animationen inte når 60fps. Kontrollera: använder du CAMediaTimingFunction (inte timer) i iOS, PathInterpolator (inte Handler.post) i Android. Tunga beräkningar i huvudtråden, komplex lagerhierarki eller stor drawable kan orsaka bildbortfall. I iOS, använd Core Animation med shouldRasterize för komplexa lager; i Android — Hardware Acceleration och GPU-rendering.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också