Главни закључци
Easing (функција заглађивања) — математичка функција f(t) која пресликава линеарни ток времена t ∈ [0,1] у нелинеарну вредност анимације. Ако анимација мења положај објекта од A до B за 500 ms, easing одређује како се објекат креће између ових тачака у сваком тренутку времена. Линеарни easing (f(t) = t) значи константну брзину — објекат се креће механички. Нелинеарни easing — природно: објекат убрзава (ease-in), успорава (ease-out) или има гладак почетак и крај (ease-in-out).
Easing функције се описују кубичним Безјеовим кривама (Cubic Bézier), где је X оса нормализовано време, а Y оса нормализовани напредак. Две контролне тачке (P1, P2) одређују облик криве. Стандардне криве су фиксиране 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). Исте вредности се користе у CAMediaTimingFunction у iOS-у и PathInterpolator у Android-у. Према Material Design Motion смерницама, easing криве треба да одговарају контексту: за појављивање — ease-out (брз старт), за нестајање — ease-in (брз финиш).
У iOS-у главни рад са easing-ом се обавља кроз класу CAMediaTimingFunction из Core Animation-а. Она прима кубичну Безјеову криву у облику две контролне тачке (c1x, c1y, c2x, c2y) и примењује се на CAAnimation (CABasicAnimation, CAKeyframeAnimation). CAMediaTimingFunction подржава 5 предефинисаних функција: linear, easeIn, easeOut, easeInEaseOut и default (еквивалент easeInEaseOut).
CAMediaTimingFunction ради на нивоу CALayer-а — анимација се извршава у одвојеном процесу render server-а, што гарантује 60fps чак и при сложеним трансформацијама. За анимацију својстава UIView (frame, alpha, transform), UIKit користи UIView.animate са параметром options: .curveEaseInOut, који унутрашњи такође креира CAMediaTimingFunction са одговарајућом кривом. У iOS 17+ додат је UIViewAnimationCurve.custom са BlockParameters за прилагођену Безјеову криву — ово елиминише потребу за писањем сопствене CAAnimation. CAMediaTimingFunction се такође користи у UIKit Dynamics и UIScrollView за декелерацију (UIScrollViewDecelerationRate).
У SwiftUI-ју easing се задаје кроз структуру Animation са модификаторима .easeIn, .easeOut, .easeInOut, .spring и .interpolatingSpring. За разлику од UIKit-а, где је анимација процедурална (beginAnimations/commitAnimations), SwiftUI користи декларативни приступ: анимација је својство стања које се мења, а фрејмворк сам одређује како да анимира прелаз.
Модификатор .timingCurve(c0x, c0y, c1x, c1y) директно прима контролне тачке кубичне Безјеове криве — ово је потпуни аналог CAMediaTimingFunction-а. SwiftUI Animation подржава предефинисане криве као статичка својства: Animation.easeInOut је еквивалент ease-in-out (0.42, 0.0, 0.58, 1.0). За spring анимацију користи се Animation.spring(response:dampingFraction:blendDuration), где dampingFraction < 1 ствара ефекат опруге са пригушењем. На WWDC 2023, Apple је представио Animation.keyframes — кадар-по-кадар анимацију са прилагођеним easing-ом на сваком кључном кадру, што даје потпуну контролу над путањом.
У Android-у easing је имплементиран кроз PathInterpolator (API 21+) — класу која прима Path објекат са кубичном Безјеовом кривом. Контролне тачке се задају кроз Path.cubicTo(x1, y1, x2, y2, x3, y3), где су (x1,y1) прва контролна тачка, (x2,y2) друга, (x3,y3) крајња тачка (увек 1,1). PathInterpolator је обавезан ако је потребна прилагођена крива; за стандардне сценарије доступни су AccelerateDecelerateInterpolator (ease-in-out), AccelerateInterpolator (ease-in) и DecelerateInterpolator (ease-out).
У Jetpack Compose-у easing се задаје кроз објекат Easing из пакета androidx.compose.animation. Доступни су FastOutSlowInEasing, LinearOutSlowInEasing, FastOutLinearInEasing, као и прилагођени Easing кроз CubicBezierEasing. Compose Easing се примењује у animate*AsState, AnimatedVisibility и AnimatedContent. Кључна разлика од View система: Compose интерполира easing на страни Kotlin-а (не у RenderNode-у), што даје више флексибилности за прилагођене анимације, али захтева оптимизацију на 120Hz дисплејима.
Основна анимација позиције слоја са прилагођеном кривом ease-in-out. CAMediaTimingFunction прима контролне тачке кубичне Безјеове криве које дефинишу гладак почетак и крај покрета.
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")
}
Контролне тачке (0.42, 0.0, 0.58, 1.0) дефинишу стандардну криву ease-in-out. SwiftUI користи исте тачке за Animation.easeInOut. За ease-in користите (0.42, 0.0, 1.0, 1.0), за ease-out — (0.0, 0.0, 0.58, 1.0). Core Animation извршава анимацију у render server-у са фреквенцијом 60/120 fps.
Декларативна анимација у SwiftUI-ју са прилагођеном кривом и spring ефектом. Модификатор animation је повезан са променом 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()
}
}
}
}
Модификатор .timingCurve(c0x, c0y, c1x, c1y, duration) креира прилагођену easing анимацију у SwiftUI-ју. Параметар value повезује анимацију са конкретним стањем — при промени isAnimated, SwiftUI аутоматски примењује криву. За spring анимацију користите .spring(response: 0.4, dampingFraction: 0.6) уместо timingCurve.
Еквивалент ease-in-out у Android-у кроз PathInterpolator. Контролне тачке (0.42, 0.0, 0.58, 1.0) дефинишу исту криву као у 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 са контролним тачкама (0.42, 0.0, 0.58, 1.0) је идентичан CAMediaTimingFunction ease-in-out на iOS-у. За међуплатформске пројекте користите исте контролне тачке — ово гарантује визуелно идентично понашање анимације. Material Design препоручује FastOutSlowInInterpolator (0.4, 0.0, 0.2, 1.0) као алтернативу за Android-специфичне интерфејсе.
У Compose-у easing се задаје кроз CubicBezierEasing са истим контролним тачкама. Резултат — глатко појављивање елемента са провидношћу и померањем.
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 у Compose-у прима иста 4 параметра контролних тачака. animateFloatAsState аутоматски интерполира вредност alpha са задатом кривом. За поређење: FastOutSlowInEasing у Compose-у је еквивалентан (0.4, 0.0, 0.2, 1.0) — стандардна крива Material Design 3. Прилагођене криве кроз CubicBezierEasing дају потпуну контролу за бренд анимације.
Често постављана питања
Easing — општи појам функције заглађивања, који се користи у iOS-у (CAMediaTimingFunction), вебу (CSS ease-in-out) и дизајну. Interpolator — имплементација овог концепта у Android-у (TimeInterpolator). У суштини, то је иста ствар — математичка крива брзине анимације. Разлика је само у називу и API-ју платформе. У међуплатформским пројектима термин easing се користи као универзалан.
iOS пружа CAMediaTimingFunction са 5 предефинисаних функција: linear, easeIn, easeOut, easeInEaseOut и custom (кубична Безјеова крива). У SwiftUI-ју су доступне Animation.easeIn, Animation.easeOut, Animation.easeInOut, Animation.spring и Animation.interpolatingSpring. За прилагођене криве користите Animation.timingCurve(c0x, c0y, c1x, c1y, duration:).
Да, ако поставите исту кубичну Безјеову криву. CAMediaTimingFunction у iOS-у и PathInterpolator у Android-у примају контролне тачке кубичне Безјеове криве (x1, y1, x2, y2). На пример, крива ease-in-out (0.42, 0.0, 0.58, 1.0) ће дати идентично понашање на обе платформе. Material Design користи овај приступ за међуплатформске анимације.
Користите Material Design Motion смернице: за појављивање елемената — ease-out (брз старт, гладак финиш), за нестајање — ease-in (брз финиш), за кретање између екрана — ease-in-out. Spring анимацију користите за интерактивне елементе (дугмад, картице). Избегавајте linear — изгледа неприродно.
Трзавост (jank) настаје када анимација не достиже 60fps. Проверите: да ли користите CAMediaTimingFunction (не timer) у iOS-у, PathInterpolator (не Handler.post) у Android-у. Тешка израчунавања у главној нити, сложена хијерархија слојева или велики drawable могу изазвати испуштање кадрова. У iOS-у користите Core Animation са shouldRasterize за сложене слојеве; у Android-у — Hardware Acceleration и GPU rendering.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође