Timeline — основы, управление временем анимации в приложениях

Автор: IT Sectr Опубликовано: 2026-03-02 Время чтения: 8 мин
Timeline (временная шкала) — фундаментальная концепция анимации, определяющая как долго длится анимация, в каком порядке выполняются её этапы и как время отображается на прогресс. В разработке мобильных приложений timeline управляется через длительность (duration), задержку (delay), повторения (repeat) и временные метки. В iOS анимационный timeline реализован через CACurrentMediaTime и CAMediaTiming, в Android — через ValueAnimator и Animator с параметрами duration, startDelay и repeatCount. По данным Apple Documentation, точное управление временной шкалой критически важно для синхронизации анимаций: рассинхронизация даже на 16 мс (один кадр при 60fps) воспринимается пользователем как дёрганье. В IT Sectr мы стандартизировали использование timeline через ValueAnimator в Android и CABasicAnimation с CAMediaTiming в iOS — это обеспечивает единообразное управление временем во всех проектах.

Главное

  • Timeline — концепция управления временем анимации: duration, delay, repeat, отображение времени на прогресс.
  • CAMediaTiming — протокол iOS Core Animation для управления timeline: duration, beginTime, repeatCount, speed, timeOffset.
  • ValueAnimator — центральный класс Android для работы с временной шкалой анимации через duration, startDelay, repeatCount.
  • CACurrentMediaTime — функция iOS для получения абсолютного времени в секундах, используется для синхронизации.
  • TimeInterval — тип данных в iOS (Double), представляющий интервал времени для анимационных расчётов.

Что такое Timeline?

Timeline — это модель времени анимации, которая определяет, когда анимация начинается, как долго длится и как ход времени преобразуется в изменение анимируемого параметра. В отличие от покадровой анимации (frame-by-frame), где каждый кадр задаётся вручную, timeline-анимация описывается параметрами: duration — общая длительность, startDelay — задержка перед стартом, repeatCount — количество повторений, и timingFunction — кривая скорости.

Временная шкала работает в нормализованном пространстве [0, 1] — local time или progress. Нулевое значение соответствует началу анимации, единица — завершению. Easing-функция преобразует линейный local time в фактический прогресс анимации. Если repeatCount > 1, timeline циклически сбрасывается (RESTART) или инвертируется (REVERSE). В iOS CAMediaTiming добавляет параметры speed (скорость воспроизведения, >1 ускоряет) и timeOffset (сдвиг временной шкалы). В Android Animator использует setCurrentPlayTime() для прямого управления позицией на временной шкале. По данным Material Design, анимации короче 100 мс не воспринимаются как анимация, а длиннее 500 мс — начинают раздражать. Оптимальная длительность для интерфейсных анимаций — 200–350 мс.

Timeline в iOS: CAMediaTiming и CACurrentMediaTime

В iOS управление временной шкалой анимации осуществляется через протокол CAMediaTiming, который реализуют CAAnimation и CALayer. Он предоставляет свойства: duration (длительность в секундах), beginTime (время старта относительно родительского timeline), repeatCount (количество повторений), repeatDuration (общая длительность повторений), autoreverses (автообратный ход), fillMode (поведение до/после анимации), speed (скорость воспроизведения) и timeOffset (сдвиг по шкале).

CACurrentMediaTime() — функция, возвращающая абсолютное время в секундах от последней перезагрузки устройства. Используется как базовая временная метка для синхронизации нескольких анимаций: установите beginTime = CACurrentMediaTime() + offset для запуска группы анимаций с точной задержкой. CAMediaTiming также поддерживает иерархию timeline — родительский слой может ускорять/замедлять все дочерние анимации через свойство speed. В UIKit UIViewPropertyAnimator (iOS 10+) абстрагирует временную шкалу через свойства duration, delay и колбэки startAnimation/pauseAnimation/stopAnimation. В SwiftUI Animation структура предоставляет duration как часть спецификации анимации, но прямое управление временной шкалой скрыто — для кастомного timeline используйте TimelineView (iOS 15+) с собственным scheduler.

Timeline в Android: Animator и duration

В Android управление временной шкалой централизовано в классе Animator и его наследниках (ValueAnimator, ObjectAnimator). Основные свойства timeline: duration (длительность в миллисекундах), startDelay (задержка перед стартом), repeatCount (количество повторений), repeatMode (RESTART или REVERSE). Метод setCurrentPlayTime(long) позволяет перемещаться по временной шкале в произвольную позицию.

ValueAnimator добавляет уровень абстракции: он генерирует значения от 0 до 1 (normalized time) на основе duration и передаёт их в TimeInterpolator. AnimatorSet позволяет комбинировать несколько Animator в единую временную шкалу с последовательным (playSequentially) или параллельным (playTogether) выполнением. В Jetpack Compose временная шкала управляется через AnimationSpec: tween (durationMillis, delayMillis, easing), spring (dampingRatio, stiffness) и keyframes (точки на временной шкале с кастомным easing для каждого отрезка). Compose AnimatedContent использует timeline для transitionSpec — разработчик описывает, как контент появляется/исчезает с временными сдвигами. По данным Android Performance, временная шкала анимации должна быть кратной 16 мс (vsync-цикл) для 60fps — неправильная длительность может вызывать пропуск кадров из-за несовпадения с refresh rate дисплея.

Композиция анимаций во времени

Временная шкала становится особенно важной при композиции нескольких анимаций. Три базовых паттерна: каскад — анимации запускаются последовательно со сдвигом (каждая следующая начинается через offset после предыдущей), параллель — все анимации запускаются одновременно с одинаковым timeline, и stagger — анимации запускаются с перекрытием (вторая начинается до завершения первой).

В iOS каскад реализуется через beginTime каждой CAAnimation = предыдущая beginTime + предыдущая duration. Для stagger используется CAAnimationGroup с разными beginTime у вложенных анимаций. В Android AnimatorSet поддерживает playSequentially и playTogether; stagger создаётся установкой startDelay для каждого Animator. В Compose AnimatedVisibility с enter/exitTransition использует stagger в варианте staggerChildren для каскадного появления элементов списка. В WWDC и Google I/O 2024 обе платформы представили инструменты визуализации timeline: Xcode Animations Inspector и Android Studio Animations Timeline — они показывают фактическую временную шкалу и пропущенные кадры в реальном времени.

Примеры кода

iOS: CABasicAnimation с CAMediaTiming

Пример анимации с кастомным timeline: задержка 0.3 сек, длительность 0.6 сек, autoreverse и бесконечный повтор. CAMediaTiming управляет всеми временными параметрами.

swift
import UIKit

func breathingAnimation(layer: CALayer) {
    let animation = CABasicAnimation(keyPath: "opacity")
    animation.duration = 0.6
    animation.beginTime = CACurrentMediaTime() + 0.3
    animation.fromValue = 1.0
    animation.toValue = 0.3
    animation.autoreverses = true
    animation.repeatCount = .infinity
    animation.speed = 1.0
    animation.fillMode = .both

    layer.add(animation, forKey: "breathing")
}

beginTime = CACurrentMediaTime() + 0.3 даёт задержку 300 мс. autoreverses = true заставляет анимацию идти в обратном направлении после каждого цикла. repeatCount = .infinity создаёт бесконечный цикл «дышащего» эффекта. speed = 1.0 — нормальная скорость; ускорьте анимацию в 2 раза значением 2.0.

iOS: TimelineView для кастомной временной шкалы

SwiftUI TimelineView (iOS 15+) предоставляет доступ к системному timeline для кастомной анимации. View обновляется при каждом тике scheduler.

swift
import SwiftUI

struct TimelineViewExample: View {
    var body: some View {
        TimelineView(.periodic(from: .now, by: 0.1)) { context in
            let seconds = context.date.timeIntervalSinceReferenceDate
            let phase = sin(seconds * 2 * .pi)

            Circle()
                .fill(Color.blue)
                .frame(width: 60 + 30 * phase)
                .animation(.linear(duration: 0), value: phase)
        }
    }
}

TimelineView с .periodic scheduler генерирует тики каждые 0.1 сек. В замыкании context.date — текущее время, которое преобразуется в фазу синусоиды. Размер круга пульсирует с частотой 1 Гц. TimelineView — единственный способ создать анимацию с собственной временной шкалой в SwiftUI без Animation API.

Android: ValueAnimator с кастомным timeline

ValueAnimator с полным управлением временной шкалой: задержка, длительность, повторение. Метод setCurrentPlayTime позволяет прыгать по временной шкале.

kotlin
import android.animation.ValueAnimator
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.View

fun View.timelineAnimation() {
    ValueAnimator.ofFloat(0f, 1f).apply {
        duration = 400
        startDelay = 200
        repeatCount = 2
        repeatMode = ValueAnimator.REVERSE
        interpolator = AccelerateDecelerateInterpolator()

        addUpdateListener { animator ->
            val progress = animator.animatedFraction
            alpha = progress
            translationX = progress * 100f
        }

        start()
    }
}

startDelay = 200 мс — задержка перед стартом. repeatMode = REVERSE возвращает анимацию к начальному значению. animatedFraction даёт прогресс от 0 до 1 с учётом repeatMode. addUpdateListener срабатывает каждый кадр (обычно 16 мс). Для приостановки используйте pause() / resume().

Android: AnimatorSet для композиции timeline

AnimatorSet позволяет создавать сложные временные шкалы из нескольких анимаций. В примере — последовательное выполнение с перекрытием (stagger).

kotlin
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.view.View

fun View.staggerAnimation() {
    val fadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f).apply {
        duration = 300
    }
    val slideUp = ObjectAnimator.ofFloat(this, "translationY", 100f, 0f).apply {
        duration = 400
        startDelay = 100
    }
    val scale = ObjectAnimator.ofFloat(this, "scaleX", 0.8f, 1f).apply {
        duration = 250
        startDelay = 200
    }

    AnimatorSet().apply {
        playTogether(fadeIn, slideUp, scale)
        start()
    }
}

playTogether запускает все анимации параллельно, но каждая имеет свой startDelay — это создаёт stagger-эффект: fadeIn стартует первым (0 мс), slideUp через 100 мс, scale через 200 мс. Итоговая временная шкала: 0 — 100 — 200 — 600 мс. Для последовательного выполнения используйте playSequentially.

Часто задаваемые вопросы

Чем Animator отличается от ValueAnimator в Android?

Animator — абстрактный базовый класс для всех анимаций. ValueAnimator — его подкласс, анимирующий числовые значения без привязки к объекту. ObjectAnimator (наследник ValueAnimator) анимирует конкретное свойство объекта (например, alpha или translationY). Для timeline используйте ValueAnimator как универсальный таймер с колбэком через addUpdateListener.

Как синхронизировать анимации на разных устройствах?

Используйте системные часы (CACurrentMediaTime в iOS, System.nanoTime в Android) для расчёта прогресса, а не количество кадров. Для сетевой синхронизации применяйте NTP-based таймер с коррекцией offset. iOS поддерживает AVAudioSession для синхронизации аудио-видео timeline с точностью до миллисекунд. Избегайте синхронизации по кадрам — частота кадров варьируется от 30 до 120 fps.

Что такое startOffset и repeatCount в Animator?

startOffset — задержка перед началом анимации в миллисекундах. Используется для каскада и stagger-эффектов. repeatCount — количество повторений: 0 = не повторяется, ValueAnimator.INFINITE = бесконечный цикл. repeatMode определяет направление: RESTART (сброс к началу) или REVERSE (обратный ход). В iOS startOffset называется beginTime (в комбинации с CACurrentMediaTime), repeatCount — одноимённое свойство CABasicAnimation.

Какая оптимальная длительность анимации?

Material Design рекомендует 200–350 мс для интерфейсных анимаций. Анимации короче 100 мс не воспринимаются как анимация, длиннее 500 мс — раздражают. Для входа/выхода элементов: 200–300 мс. Для перемещения между экранами: 300–400 мс. Используйте более короткие длительности для мелких элементов (100–200 мс) и более длинные для крупных (400–500 мс).

Почему анимация дёргается при воспроизведении?

Дёрганье (jank) возникает из-за: длительных операций в главном потоке (блокируют рендеринг), сложной иерархии View/Layer с overdraw, или неправильного использования timeline (длительность не кратна vsync — 16ms для 60fps, 8ms для 120fps). Используйте Xcode Instruments Animations или Android Studio Profiler для обнаружения пропущенных кадров. В Compose применяйте Modifier.drawWithCache для кэширования сложных отрисовок.

Итоги

  • Timeline — модель времени анимации с параметрами duration, startDelay, repeatCount, repeatMode, определяющая ход анимации.
  • iOS CAMediaTiming протокол управляет временной шкалой через duration, beginTime, speed, timeOffset, repeatCount, fillMode.
  • Android Animator и ValueAnimator предоставляют duration, startDelay, repeatCount, repeatMode и setCurrentPlayTime для прямого управления.
  • AnimatorSet в Android и CAAnimationGroup в iOS позволяют комбинировать анимации в единую временную шкалу.
  • TimelineView в SwiftUI (iOS 15+) даёт доступ к системному scheduler для кастомных временных шкал без Animation API.
  • CACurrentMediaTime() в iOS и System.nanoTime в Android — базовые временные метки для синхронизации анимаций.
  • Оптимальная длительность интерфейсных анимаций — 200-350 мс согласно Material Design и HIG рекомендациям.

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

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

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