Реализация анимации чарта/графика в мобильном приложении
Статичный график — это просто данные. Анимированный — это история. Когда столбцы в bar chart поднимаются снизу вверх последовательно, а линия на line chart «рисуется» слева направо при загрузке экрана, пользователь проходит вместе с данными, а не просто считывает цифры. Но за этим эффектом стоит нетривиальная техническая работа.
Подходы к реализации
Первый вопрос: использовать готовую библиотеку или писать на Canvas/Core Graphics?
Библиотеки:
- iOS: Charts (DGCharts), SwiftCharts (нативный, iOS 16+)
- Android: MPAndroidChart, Vico
- Flutter: fl_chart, syncfusion_flutter_charts
- React Native: Victory Native, react-native-chart-kit
Библиотеки дают 80% нужного за 20% времени, но кастомизация анимации в них ограничена. MPAndroidChart поддерживает animateX() и animateY(), но настроить easing per-bar или сделать stagger-анимацию (столбцы появляются поочерёдно) — нельзя без кастомного Renderer.
Кастомная реализация на Canvas нужна когда: нестандартный тип чарта, stagger-эффект, анимация отдельных точек по данным, интерактивный tooltip с анимацией.
Stagger animation для bar chart
На Flutter через AnimationController + Interval:
// Каждый столбец анимируется с задержкой 80мс
for (int i = 0; i < bars.length; i++) {
final start = (i * 0.08).clamp(0.0, 1.0);
final end = (start + 0.4).clamp(0.0, 1.0);
animations[i] = Tween(begin: 0.0, end: bars[i].value).animate(
CurvedAnimation(
parent: controller,
curve: Interval(start, end, curve: Curves.easeOutCubic),
),
);
}
На iOS через CAKeyframeAnimation с keyTimes — каждый CAShapeLayer (один столбец) получает смещённый beginTime:
bars.enumerated().forEach { index, layer in
let anim = CABasicAnimation(keyPath: "bounds.size.height")
anim.beginTime = CACurrentMediaTime() + Double(index) * 0.08
anim.duration = 0.4
anim.timingFunction = CAMediaTimingFunction(name: .easeOut)
layer.add(anim, forKey: nil)
}
Анимация line chart: strokeEnd
Line chart рисуется через CAShapeLayer с strokeEnd: 0 → 1. Для добавления точек-маркеров, которые появляются в момент прохождения «кисти» — нужно синхронизировать появление CALayer с прогрессом strokeEnd. Реализуем через CAAnimationDelegate.animationDidStop для каждого сегмента или через displayLink с отслеживанием presentation().strokeEnd.
Интерактивный tooltip
Tooltip, следующий за пальцем по графику — отдельная задача. На iOS: UIGestureRecognizer → пересчёт координаты в значение данных → UIView.animate для tooltip. Важно использовать setNeedsDisplay() только для той части CALayer, где рисуется highlight-линия — перерисовка всего canvas убивает FPS при быстром движении пальца.
Процесс работы
Получаем формат данных и Figma-дизайн чарта. Определяем тип реализации: библиотека или кастомный Canvas. Реализуем базовый рендер → добавляем анимацию появления → тестируем производительность через Instruments / Android Profiler (цель: 60fps при любом размере датасета). При большом объёме данных (500+ точек) обязательно применяем downsampling алгоритм Ramer–Douglas–Peucker.
Срок: 1–3 дня в зависимости от типа и сложности чарта.







