Easing: esența, funcțiile de netezire în animația mobilă

Autor: IT Sectr Publicat: 2026-03-02 Timp de citire: 8 min
Easing (funcția de netezire) — o curbă matematică care determină viteza de modificare a parametrului de animație în timp. În locul mișcării liniare uniform accelerate, easing imită comportamentul fizic: obiectul poate accelera (ease-in), încetini (ease-out) sau face ambele (ease-in-out). În iOS, easing este implementat prin clasa CAMediaTimingFunction (Core Animation) și modificatorii SwiftUI (Animation.easeInOut). În Android — prin PathInterpolator și TimeInterpolator. Conform documentației Apple Developer, o curbă de easing corect aleasă reduce timpul perceput al animației cu 20–35% fără a modifica durata reală. La IT Sectr am standardizat curba ease-in-out (0.42, 0.0, 0.58, 1.0) ca standard corporativ pentru toate proiectele mobile.

Principalele concluzii

  • Easing — funcția de viteză a animației care imită mișcarea fizică: accelerare, încetinire sau combinație.
  • CAMediaTimingFunction — clasa iOS Core Animation cu 5 curbe predefinite și Bézier cubic personalizat.
  • SwiftUI Animation — modificatorii .easeIn, .easeOut, .easeInOut, .spring și .timingCurve pentru easing declarativ.
  • PathInterpolator — analogul easing-ului în Android cu curbă Bézier cubică prin două puncte de control.
  • Bézier cubic — formatul universal de definire a curbei easing, comun pentru iOS, Android și web.

Ce este Easing?

Easing (funcția de netezire) — o funcție matematică f(t) care mapează progresul liniar al timpului t ∈ [0,1] la o valoare neliniară a animației. Dacă animația modifică poziția unui obiect de la A la B în 500 ms, easing determină modul în care obiectul se mișcă între aceste puncte în fiecare moment de timp. Easing liniar (f(t) = t) înseamnă viteză constantă — obiectul se mișcă mecanic. Easing neliniar — natural: obiectul accelerează (ease-in), încetinește (ease-out) sau are un început și sfârșit lin (ease-in-out).

Funcțiile de easing sunt descrise prin curbe Bézier cubice (Cubic Bézier), unde axa X este timpul normalizat, iar axa Y este progresul normalizat. Două puncte de control (P1, P2) determină forma curbei. Curbele standard sunt fixate de specificația 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). Aceleași valori sunt utilizate în CAMediaTimingFunction în iOS și PathInterpolator în Android. Conform ghidurilor Material Design Motion, curbele de easing trebuie să corespundă contextului: pentru apariție — ease-out (start rapid), pentru dispariție — ease-in (final rapid).

Easing în iOS: CAMediaTimingFunction

În iOS, lucrul principal cu easing se face prin clasa CAMediaTimingFunction din Core Animation. Aceasta primește o curbă Bézier cubică sub forma a două puncte de control (c1x, c1y, c2x, c2y) și se aplică la CAAnimation (CABasicAnimation, CAKeyframeAnimation). CAMediaTimingFunction suportă 5 funcții predefinite: linear, easeIn, easeOut, easeInEaseOut și default (echivalentul easeInEaseOut).

CAMediaTimingFunction funcționează la nivelul CALayer — animația este executată într-un proces separat, render server, ceea ce garantează 60fps chiar și la transformări complexe. Pentru animația proprietăților UIView (frame, alpha, transform), UIKit utilizează UIView.animate cu parametrul options: .curveEaseInOut, care intern creează și el CAMediaTimingFunction cu curba corespunzătoare. În iOS 17+ a fost adăugat UIViewAnimationCurve.custom cu BlockParameters pentru Bézier personalizat — aceasta elimină necesitatea de a scrie propria CAAnimation. CAMediaTimingFunction este, de asemenea, utilizată în UIKit Dynamics și UIScrollView pentru decelerare (UIScrollViewDecelerationRate).

Easing în SwiftUI

În SwiftUI, easing este definit prin structura Animation cu modificatorii .easeIn, .easeOut, .easeInOut, .spring și .interpolatingSpring. Spre deosebire de UIKit, unde animația este procedurală (beginAnimations/commitAnimations), SwiftUI utilizează o abordare declarativă: animația este o proprietate de stare care se modifică, iar framework-ul determină singur cum să animeze tranziția.

Modificatorul .timingCurve(c0x, c0y, c1x, c1y) primește direct punctele de control ale Bézier-ului cubic — este analogul complet al CAMediaTimingFunction. SwiftUI Animation suportă curbele predefinite ca proprietăți statice: Animation.easeInOut este echivalent cu ease-in-out (0.42, 0.0, 0.58, 1.0). Pentru animația cu arc se utilizează Animation.spring(response:dampingFraction:blendDuration), unde dampingFraction < 1 creează un efect de arc cu amortizare. La WWDC 2023, Apple a prezentat Animation.keyframes — animație cadru cu cadru cu easing personalizat pe fiecare cadru cheie, oferind control complet asupra traiectoriei.

Easing în Android: PathInterpolator

În Android, easing este implementat prin PathInterpolator (API 21+) — o clasă care primește un obiect Path cu o curbă Bézier cubică. Punctele de control sunt definite prin Path.cubicTo(x1, y1, x2, y2, x3, y3), unde (x1,y1) este primul punct de control, (x2,y2) al doilea, (x3,y3) punctul final (întotdeauna 1,1). PathInterpolator este obligatoriu dacă este necesară o curbă personalizată; pentru scenarii standard sunt disponibile AccelerateDecelerateInterpolator (ease-in-out), AccelerateInterpolator (ease-in) și DecelerateInterpolator (ease-out).

În Jetpack Compose, easing este definit prin obiectul Easing din pachetul androidx.compose.animation. Sunt disponibile FastOutSlowInEasing, LinearOutSlowInEasing, FastOutLinearInEasing, precum și Easing personalizat prin CubicBezierEasing. Compose Easing se aplică în animate*AsState, AnimatedVisibility și AnimatedContent. Diferența cheie față de sistemul View: Compose interpolează easing pe partea Kotlin (nu în RenderNode), ceea ce oferă mai multă flexibilitate pentru animații personalizate, dar necesită optimizare la display-urile de 120Hz.

Exemple de cod

iOS: CAMediaTimingFunction cu CABasicAnimation

Animația de bază a poziției stratului cu o curbă personalizată ease-in-out. CAMediaTimingFunction primește punctele de control ale Bézier-ului cubic, definind un început și un sfârșit lin al mișcării.

swift
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")
}

Punctele de control (0.42, 0.0, 0.58, 1.0) definesc curba standard ease-in-out. SwiftUI utilizează aceleași puncte pentru Animation.easeInOut. Pentru ease-in utilizați (0.42, 0.0, 1.0, 1.0), pentru ease-out — (0.0, 0.0, 0.58, 1.0). Core Animation execută animația în render server cu o frecvență de 60/120 fps.

SwiftUI: Easing cu timingCurve

Animația declarativă în SwiftUI cu o curbă personalizată și efect de arc. Modificatorul animation este legat de modificarea isAnimated.

swift
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()
            }
        }
    }
}

Modificatorul .timingCurve(c0x, c0y, c1x, c1y, duration) creează o animație easing personalizată în SwiftUI. Parametrul value leagă animația de o anumită stare — la modificarea isAnimated, SwiftUI aplică automat curba. Pentru animația cu arc, utilizați .spring(response: 0.4, dampingFraction: 0.6) în loc de timingCurve.

Android: PathInterpolator cu easing personalizat

Echivalentul ease-in-out în Android prin PathInterpolator. Punctele de control (0.42, 0.0, 0.58, 1.0) definesc aceeași curbă ca în iOS.

kotlin
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 cu punctele de control (0.42, 0.0, 0.58, 1.0) este identic cu CAMediaTimingFunction ease-in-out pe iOS. Pentru proiecte cross-platform, utilizați aceleași puncte de control — aceasta garantează un comportament vizual identic al animației. Material Design recomandă FastOutSlowInInterpolator (0.4, 0.0, 0.2, 1.0) ca alternativă pentru interfețe specifice Android.

Jetpack Compose: Easing cu CubicBezierEasing

În Compose, easing este definit prin CubicBezierEasing cu aceleași puncte de control. Rezultatul — o apariție lină a elementului cu transparență și deplasare.

kotlin
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 în Compose primește aceiași 4 parametri ai punctelor de control. animateFloatAsState interpolează automat valoarea alpha cu curba dată. Pentru comparație: FastOutSlowInEasing în Compose este echivalent cu (0.4, 0.0, 0.2, 1.0) — curba standard Material Design 3. Curbele personalizate prin CubicBezierEasing oferă control complet pentru animații de brand.

Întrebări frecvente

Cu ce se deosebește easing de interpolator?

Easing — conceptul general al funcției de netezire, utilizat în iOS (CAMediaTimingFunction), web (CSS ease-in-out) și design. Interpolator — implementarea acestui concept în Android (TimeInterpolator). În esență, sunt același lucru — o curbă matematică a vitezei de animație. Diferența constă doar în denumire și API-ul platformei. În proiectele cross-platform, termenul easing este utilizat ca unul universal.

Ce funcții de easing sunt disponibile în iOS?

iOS oferă CAMediaTimingFunction cu 5 funcții predefinite: linear, easeIn, easeOut, easeInEaseOut și custom (Bézier cubic). În SwiftUI sunt disponibile Animation.easeIn, Animation.easeOut, Animation.easeInOut, Animation.spring și Animation.interpolatingSpring. Pentru curbe personalizate, utilizați Animation.timingCurve(c0x, c0y, c1x, c1y, duration:).

Pot folosi aceeași funcție de easing pe iOS și Android?

Da, dacă setați aceeași curbă Bézier cubică. CAMediaTimingFunction în iOS și PathInterpolator în Android acceptă punctele de control ale Bézier-ului cubic (x1, y1, x2, y2). De exemplu, curba ease-in-out (0.42, 0.0, 0.58, 1.0) va avea același comportament pe ambele platforme. Material Design utilizează această abordare pentru animații cross-platform.

Cum aleg curba de easing potrivită pentru aplicația mea?

Utilizați ghidurile Material Design Motion: pentru apariția elementelor — ease-out (start rapid, final lin), pentru dispariție — ease-in (final rapid), pentru deplasarea între ecrane — ease-in-out. Utilizați animația cu arc pentru elemente interactive (butoane, carduri). Evitați linear — arată nenatural.

De ce animația mea cu easing pare sacadată?

Sacadarea (jank) apare atunci când animația nu atinge 60fps. Verificați: dacă utilizați CAMediaTimingFunction (nu timer) în iOS, PathInterpolator (nu Handler.post) în Android. Calculele grele pe firul principal, ierarhia complexă a straturilor sau un drawable mare pot cauza pierderi de cadre. În iOS, utilizați Core Animation cu shouldRasterize pentru straturi complexe; în Android — Hardware Acceleration și GPU rendering.

Rezumat

  • Easing — funcția de viteză a animației care face mișcarea naturală prin modificarea neliniară a parametrilor în timp.
  • iOS CAMediaTimingFunction și SwiftUI Animation.timingCurve implementează easing prin curba Bézier cubică cu două puncte de control.
  • Android PathInterpolator și Compose CubicBezierEasing — analogii completi ai iOS pentru curbe de netezire personalizate.
  • Curbele de easing cross-platform sunt definite prin puncte de control identice — baza unificării animațiilor.
  • Material Design 3 recomandă fast-out-slow-in pentru apariție, linear-out-slow-in pentru dispariție.
  • Animația cu arc în SwiftUI (Animation.spring) și Compose (spring()) creează un efect fizic de arc cu dampingFraction.
  • Easing liniar nu este recomandat pentru animațiile de interfață — arată nenatural și mecanic.

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și