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-функции описываются кубическими кривыми Безье (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 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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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