Puntos clave
Easing (función de suavizado) es una función matemática f(t) que asigna el tiempo lineal t ∈ [0,1] a un valor de animación no lineal. Si una animación cambia la posición de un objeto de A a B en 500 ms, el easing determina cómo se mueve el objeto entre estos puntos en cada momento del tiempo. El easing lineal (f(t) = t) significa velocidad constante — el objeto se mueve mecánicamente. El easing no lineal se siente natural: el objeto acelera (ease-in), desacelera (ease-out) o tiene un inicio y final suaves (ease-in-out).
Las funciones de easing se describen mediante curvas Bézier cúbicas, donde el eje X es el tiempo normalizado y el eje Y es el progreso normalizado. Dos puntos de control (P1, P2) definen la forma de la curva. Las curvas estándar están fijadas por la especificación 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). Estos mismos valores se usan en CAMediaTimingFunction en iOS y PathInterpolator en Android. Según las Guías de Diseño de Movimiento de Material Design, las curvas de easing deben coincidir con el contexto: para aparición — ease-out (inicio rápido), para desaparición — ease-in (final rápido).
En iOS, el easing funciona principalmente a través de la clase CAMediaTimingFunction de Core Animation. Acepta una curva Bézier cúbica como dos puntos de control (c1x, c1y, c2x, c2y) y se aplica a CAAnimation (CABasicAnimation, CAKeyframeAnimation). CAMediaTimingFunction soporta 5 funciones predefinidas: linear, easeIn, easeOut, easeInEaseOut y default (equivalente a easeInEaseOut).
CAMediaTimingFunction funciona a nivel de CALayer — la animación se ejecuta en un proceso separado del servidor de renderizado, garantizando 60fps incluso con transformaciones complejas. Para la animación de propiedades de UIView (frame, alpha, transform), UIKit usa UIView.animate con el parámetro options: .curveEaseInOut, que internamente también crea una CAMediaTimingFunction con la curva correspondiente. En iOS 17+, se agregó UIViewAnimationCurve.custom con BlockParameters para Bézier personalizada — esto elimina la necesidad de escribir CAAnimation personalizada. CAMediaTimingFunction también se usa en UIKit Dynamics y UIScrollView para la deceleración (UIScrollViewDecelerationRate).
En SwiftUI, el easing se configura a través de la estructura Animation con los modificadores .easeIn, .easeOut, .easeInOut, .spring y .interpolatingSpring. A diferencia de UIKit, donde la animación es procedural (beginAnimations/commitAnimations), SwiftUI utiliza un enfoque declarativo: la animación es una propiedad de estado que cambia, y el framework determina cómo animar la transición.
El modificador .timingCurve(c0x, c0y, c1x, c1y) acepta puntos de control Bézier cúbica directamente — es un análogo completo de CAMediaTimingFunction. SwiftUI Animation soporta curvas predefinidas como propiedades estáticas: Animation.easeInOut es equivalente a ease-in-out (0.42, 0.0, 0.58, 1.0). Para la animación spring, se usa Animation.spring(response:dampingFraction:blendDuration), donde dampingFraction < 1 crea un efecto de resorte con amortiguación. En WWDC 2023, Apple presentó Animation.keyframes — animación fotograma a fotograma con easing personalizado en cada fotograma clave, dando control total sobre la trayectoria.
En Android, el easing se implementa a través de PathInterpolator (API 21+) — una clase que acepta un objeto Path con una curva Bézier cúbica. Los puntos de control se especifican mediante Path.cubicTo(x1, y1, x2, y2, x3, y3), donde (x1,y1) es el primer punto de control, (x2,y2) es el segundo y (x3,y3) es el punto final (siempre 1,1). PathInterpolator es obligatorio si se necesita una curva personalizada; para escenarios estándar, están disponibles AccelerateDecelerateInterpolator (ease-in-out), AccelerateInterpolator (ease-in) y DecelerateInterpolator (ease-out).
En Jetpack Compose, el easing se configura mediante el objeto Easing del paquete androidx.compose.animation. Están disponibles FastOutSlowInEasing, LinearOutSlowInEasing, FastOutLinearInEasing, así como Easing personalizado a través de CubicBezierEasing. El easing de Compose se aplica en animate*AsState, AnimatedVisibility y AnimatedContent. La diferencia clave con el sistema View: Compose interpola el easing en el lado de Kotlin (no en RenderNode), lo que brinda más flexibilidad para animaciones personalizadas pero requiere optimización en pantallas de 120Hz.
Animación básica de posición de capa con una curva ease-in-out personalizada. CAMediaTimingFunction acepta puntos de control Bézier cúbica que definen un inicio y final suaves del movimiento.
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")
}
Los puntos de control (0.42, 0.0, 0.58, 1.0) definen la curva estándar ease-in-out. SwiftUI usa los mismos puntos para Animation.easeInOut. Para ease-in, use (0.42, 0.0, 1.0, 1.0); para ease-out — (0.0, 0.0, 0.58, 1.0). Core Animation ejecuta la animación en el servidor de renderizado a 60/120 fps.
Animación declarativa en SwiftUI con una curva personalizada y efecto spring. El modificador animation se vincula al cambio de 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()
}
}
}
}
El modificador .timingCurve(c0x, c0y, c1x, c1y, duration) crea una animación de easing personalizada en SwiftUI. El parámetro value vincula la animación a un estado específico — cuando isAnimated cambia, SwiftUI aplica automáticamente la curva. Para animación spring, use .spring(response: 0.4, dampingFraction: 0.6) en lugar de timingCurve.
El equivalente de ease-in-out en Android mediante PathInterpolator. Los puntos de control (0.42, 0.0, 0.58, 1.0) definen la misma curva que en 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 con puntos de control (0.42, 0.0, 0.58, 1.0) es idéntico a CAMediaTimingFunction ease-in-out en iOS. Para proyectos multiplataforma, use los mismos puntos de control — esto garantiza un comportamiento de animación visualmente idéntico. Material Design recomienda FastOutSlowInInterpolator (0.4, 0.0, 0.2, 1.0) como alternativa para interfaces específicas de Android.
En Compose, el easing se configura mediante CubicBezierEasing con los mismos puntos de control. El resultado es una aparición suave del elemento con transparencia y desplazamiento.
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 en Compose acepta los mismos 4 parámetros de puntos de control. animateFloatAsState interpola automáticamente el valor alpha con la curva especificada. En comparación: FastOutSlowInEasing en Compose es equivalente a (0.4, 0.0, 0.2, 1.0) — la curva estándar de Material Design 3. Las curvas personalizadas mediante CubicBezierEasing brindan control total para animaciones de marca.
Preguntas frecuentes
Easing es un concepto general de función de suavizado utilizado en iOS (CAMediaTimingFunction), la web (CSS ease-in-out) y el diseño. Interpolator es la implementación de este concepto en Android (TimeInterpolator). En esencia son lo mismo — una curva matemática de velocidad de animación. La diferencia está solo en el nombre y la API de la plataforma. En proyectos multiplataforma, el término easing se usa como universal.
iOS proporciona CAMediaTimingFunction con 5 funciones predefinidas: linear, easeIn, easeOut, easeInEaseOut y custom (Bézier cúbica). En SwiftUI están disponibles Animation.easeIn, Animation.easeOut, Animation.easeInOut, Animation.spring y Animation.interpolatingSpring. Para curvas personalizadas, use Animation.timingCurve(c0x, c0y, c1x, c1y, duration:).
Sí, si se configura la misma curva Bézier cúbica. CAMediaTimingFunction en iOS y PathInterpolator en Android aceptan puntos de control Bézier cúbica (x1, y1, x2, y2). Por ejemplo, la curva ease-in-out (0.42, 0.0, 0.58, 1.0) dará un comportamiento idéntico en ambas plataformas. Material Design utiliza este enfoque para animaciones multiplataforma.
Use las Guías de Movimiento de Material Design: para la aparición de elementos — ease-out (inicio rápido, final suave), para la desaparición — ease-in (final rápido), para el movimiento entre pantallas — ease-in-out. Use animación spring para elementos interactivos (botones, tarjetas). Evite linear — se ve antinatural.
El entrecortamiento (jank) ocurre cuando la animación no alcanza los 60fps. Verifique: ¿está usando CAMediaTimingFunction (no un timer) en iOS, PathInterpolator (no Handler.post) en Android. Los cálculos pesados en el hilo principal, una jerarquía de capas compleja o un drawable grande pueden causar caídas de fotogramas. En iOS, use Core Animation con shouldRasterize para capas complejas; en Android — Hardware Acceleration y renderizado por GPU.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también