Easing: функції пом'якшення в мобільній анімації

Автор: IT Sectr Опубліковано: 2026-03-02 Час читання: 8 хв
Easing (функція пом'якшення) — математична крива, яка визначає швидкість зміни анімаційного параметра в часі. Замість лінійного рівноприскореного руху easing імітує фізичну поведінку: об'єкт може прискорюватися (ease-in), сповільнюватися (ease-out) або робити і те, й інше (ease-in-out). В iOS easing реалізовано через клас CAMediaTimingFunction (Core Animation) та модифікатори SwiftUI (Animation.easeInOut). В Android — через PathInterpolator та TimeInterpolator. За даними Apple Developer Documentation, правильно підібрана крива easing скорочує сприйнятий час анімації на 20–35% без зміни фактичної тривалості. В IT Sectr ми стандартизували криву ease-in-out (0.42, 0.0, 0.58, 1.0) як корпоративний стандарт для всіх мобільних проєктів.

Головне

  • Easing — функція швидкості анімації, що імітує фізичний рух: прискорення, сповільнення або комбінацію.
  • CAMediaTimingFunction — клас iOS Core Animation з 5 попередньо визначеними кривими та кастомним кубічним Безьє.
  • SwiftUI Animation — модифікатори .easeIn, .easeOut, .easeInOut, .spring та .timingCurve для декларативного easing.
  • PathInterpolator — аналог easing в Android з кривою кубічного Безьє через дві контрольні точки.
  • Кубічний Безьє — універсальний формат задання кривої easing, спільний для iOS, Android та вебу.

Що таке Easing?

Easing (функція пом'якшення) — математична функція f(t), яка відображає лінійний хід часу t ∈ [0,1] у нелінійне значення анімації. Якщо анімація змінює позицію об'єкта від A до B за 500 мс, easing визначає, як об'єкт рухається між цими точками в кожен момент часу. Лінійне easing (f(t) = t) означає постійну швидкість — об'єкт рухається механічно. Нелінійне easing — природно: об'єкт прискорюється (ease-in), сповільнюється (ease-out) або має плавний початок і кінець (ease-in-out).

Easing-функції описуються кубічними кривими Безьє, де вісь 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 Guidelines, easing-криві повинні відповідати контексту: для появи — ease-out (швидкий старт), для зникнення — ease-in (швидкий фінал).

Easing в iOS: CAMediaTimingFunction

В 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).

Easing в SwiftUI

В 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 на кожному ключовому кадрі, що дає повний контроль над траєкторією.

Easing в Android: PathInterpolator

В 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-дисплеях.

Приклади коду

iOS: CAMediaTimingFunction з CABasicAnimation

Базова анімація позиції шару з кастомною кривою ease-in-out. CAMediaTimingFunction приймає контрольні точки кубічного Безьє, що задають плавний початок і кінець руху.

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

Контрольні точки (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: Easing з timingCurve

Декларативна анімація в SwiftUI з кастомною кривою та spring-ефектом. Модифікатор animation прив'язується до зміни 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()
            }
        }
    }
}

Модифікатор .timingCurve(c0x, c0y, c1x, c1y, duration) створює кастомну easing-анімацію в SwiftUI. Параметр value пов'язує анімацію з конкретним станом — при зміні isAnimated SwiftUI автоматично застосовує криву. Для spring-анімації використовуйте .spring(response: 0.4, dampingFraction: 0.6) замість timingCurve.

Android: PathInterpolator з кастомним easing

Еквівалент ease-in-out в Android через PathInterpolator. Контрольні точки (0.42, 0.0, 0.58, 1.0) задають ту ж криву, що й в 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 з контрольними точками (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-специфічних інтерфейсів.

Jetpack Compose: Easing з CubicBezierEasing

В Compose easing задається через CubicBezierEasing з тими ж контрольними точками. Результат — плавна поява елемента з прозорістю та зсувом.

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 в Compose приймає ті ж 4 параметри контрольних точок. animateFloatAsState автоматично інтерполює значення alpha із заданою кривою. Для порівняння: FastOutSlowInEasing в Compose еквівалентний (0.4, 0.0, 0.2, 1.0) — стандартна крива Material Design 3. Кастомні криві через CubicBezierEasing дають повний контроль для брендових анімацій.

Часті запитання

Чим easing відрізняється від interpolator?

Easing — загальне поняття функції пом'якшення, що використовується в iOS (CAMediaTimingFunction), вебі (CSS ease-in-out) та дизайні. Interpolator — реалізація цієї концепції в Android (TimeInterpolator). По суті це одне й те ж — математична крива швидкості анімації. Різниця лише в назві та API платформи. В крос-платформених проєктах термін easing використовується як універсальний.

Які easing-функції доступні в iOS?

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:).

Чи можна використовувати одну easing-функцію на iOS та Android?

Так, якщо задати однакову кубічну криву Безьє. CAMediaTimingFunction в iOS та PathInterpolator в Android приймають контрольні точки кубічного Безьє (x1, y1, x2, y2). Наприклад, крива ease-in-out (0.42, 0.0, 0.58, 1.0) дасть ідентичну поведінку на обох платформах. Material Design використовує цей підхід для крос-платформених анімацій.

Як вибрати правильну easing-криву для мого застосунку?

Використовуйте Material Design Motion Guidelines: для появи елементів — ease-out (швидкий старт, плавний фінал), для зникнення — ease-in (швидкий фінал), для переміщення між екранами — ease-in-out. Spring-анімацію використовуйте для інтерактивних елементів (кнопки, картки). Уникайте linear — він виглядає неприродно.

Чому моя анімація з easing виглядає смикано?

Смиканість (jank) виникає, коли анімація не досягає 60fps. Перевірте: чи використовуєте ви CAMediaTimingFunction (не таймер) в iOS, PathInterpolator (не Handler.post) в Android. Важкі обчислення в основному потоці, складна ієрархія шарів або великий drawable можуть викликати пропуск кадрів. В iOS використовуйте Core Animation з shouldRasterize для складних шарів; в Android — Hardware Acceleration та GPU rendering.

Підсумки

  • Easing — функція швидкості анімації, що робить рух природним через нелінійну зміну параметрів у часі.
  • iOS CAMediaTimingFunction та SwiftUI Animation.timingCurve реалізують easing через кубічну криву Безьє з двома контрольними точками.
  • Android PathInterpolator та Compose CubicBezierEasing — повні аналоги iOS для кастомних кривих пом'якшення.
  • Крос-платформові easing-криві задаються однаковими контрольними точками — основа уніфікації анімацій.
  • Material Design 3 рекомендує fast-out-slow-in для появи, linear-out-slow-in для зникнення.
  • Spring-анімація в SwiftUI (Animation.spring) та Compose (spring()) створює фізичний ефект пружини з dampingFraction.
  • Linear easing не рекомендується для інтерфейсних анімацій — він виглядає неприродно та механічно.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також