Реализация анимированного прогресса достижений в мобильном приложении
Система достижений без анимации — это просто список чекбоксов. Когда пользователь разблокирует значок и видит плавное заполнение прогресс-кольца, вспышку частиц и анимированное появление бейджа — он запоминает этот момент. Правильно реализованная анимация превращает технический факт в эмоциональное событие.
Прогресс-кольцо с анимацией
Круговой прогресс — самый распространённый элемент систем достижений.
На iOS — CAShapeLayer с анимацией strokeEnd:
let progressLayer = CAShapeLayer()
progressLayer.path = UIBezierPath(arcCenter: center, radius: radius,
startAngle: -(.pi / 2), endAngle: 1.5 * .pi, clockwise: true).cgPath
progressLayer.strokeEnd = 0
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = previousProgress // например, 0.6
animation.toValue = newProgress // например, 0.85
animation.duration = 0.8
animation.timingFunction = CAMediaTimingFunction(name: .easeOut)
progressLayer.add(animation, forKey: "progressAnimation")
progressLayer.strokeEnd = newProgress
Важно устанавливать strokeEnd на layer ПОСЛЕ добавления анимации — иначе слой «прыгнет» в конечное состояние немедленно.
На Android — ObjectAnimator.ofFloat(progressView, "progress", from, to) с кастомным View, рисующим дугу через Canvas.drawArc. Или CircularProgressIndicator из Material 3 с setProgressCompat(value, animate: true).
Анимация разблокировки
Момент, когда достижение открывается, должен быть ярким. Типичная последовательность:
-
Shake/pulse бейджа:
CAKeyframeAnimationнаtransform.scaleсо значениями[1, 1.15, 0.95, 1.05, 1.0]— имитация «щелчка» -
Reveal анимация: бейдж появляется через circular reveal или scale от 0 до 1 с
UISpringTimingParameters(dampingRatio: 0.6) -
Частицы:
CAEmitterLayerс короткой burst-эмиссией (birthRate = 200, lifetime = 0.8) — конфетти или звёздочки -
Haptic:
UINotificationFeedbackGenerator(.success)синхронно с peak анимации
В Flutter та же последовательность через AnimationController с несколькими Tween и SequenceAnimation из пакета flutter_sequence_animation, или цепочка Future.delayed + AnimationController.forward().
Streaks и цепочки прогресса
Ежедневный streak — отдельный визуальный элемент. Каждый день-ячейка должна «загораться» последовательно (stagger), а текущий день — пульсировать через бесконечную анимацию CABasicAnimation(keyPath: "opacity") с autoreverses: true.
При достижении milestone (7 дней, 30 дней) — специальная celebratory анимация: в Flutter это showDialog с Lottie-файлом внутри, который воспроизводится однократно.
Доступность
Reduce Motion на iOS и Disable animations на Android должны быть учтены. При включённых настройках заменяем анимации на мгновенное изменение состояния без particle effects. Проверяем через UIAccessibility.isReduceMotionEnabled / Settings.Global.ANIMATOR_DURATION_SCALE == 0.
Срок: 1–3 дня в зависимости от количества анимационных состояний и платформ.







