ראינו את זה אינספור פעמים: לקוח מביא אבטיפוס עם תרשים שבו עמודות עולות בזו אחר זו וקו זורם כמו משיכת מכחול של צייר. במבט ראשון, זו אנימציה פשוטה. אבל כשזה מגיע לקוד, הספרייה המוכנה לא תומכת באפקט המדורג הזה, ויישום Canvas מותאם אישית דורש הבנה מעמיקה של Core Animation או של Android Canvas API. אנחנו לוקחים על עצמנו משימות כאלה ומיישמים אנימציות בכל רמת מורכבות תוך 1–3 ימים — בין אם זה תרשים עמודות, תרשים קו או תרשים עוגה. במאמר זה, נעבור על בעיות טיפוסיות וכיצד לפתור אותן.
לפני בחירת ספרייה או כתיבה מאפס, חשוב להבין אילו אנימציות נדרשות: הופעה פשוטה או מדורגת רציפה, טיפול באירועי מגע ו-tooltips, עבודה עם מערכי נתונים גדולים (500+ נקודות) ללא ירידת FPS. אנו משתמשים בפרופיילינג (Instruments, Android Profiler) בכל שלב כדי להבטיח 60fps.
איך לבחור בין ספרייה ל-Canvas מותאם אישית?
ההחלטה בין ספרייה מוכנה ליישום מותאם אישית היא קריטית. ספריות כמו MPAndroidChart או Swift Charts נותנות לך 80% ממה שאתה צריך ב-20% מהזמן, אבל התאמת האנימציה מוגבלת. לדוגמה, MPAndroidChart תומך ב-animateX() ו-animateY(), אבל התאמת easing לכל עמודה או יצירת אנימציה מדורגת (עמודות שמופיעות אחת אחת) בלתי אפשרית ללא Renderer מותאם אישית. Canvas מותאם אישית מוצדק כאשר אתה צריך סוג תרשים לא סטנדרטי, אפקט מדורג, אנימציה של נקודות בודדות, או tooltips אינטראקטיביים.
| קריטריון | ספרייה (MPAndroidChart, Swift Charts) | Canvas מותאם אישית |
|---|---|---|
| זמן יישום | שעות | 1–3 ימים |
| התאמת אנימציה | מוגבלת (easing, מדורג) | מלאה (כל עקומה, עיכובים) |
| ביצועים עם 500+ נקודות | בינוני (פיגור פוטנציאלי) | גבוה (דגימה מטה + LOD) |
| תמיכה במחוות | מובנית (רדודה) | ניתנת להתאמה (כל לוגיקה) |
למה אנימציה מדורגת חשובה לתפיסת הנתונים
אנימציה מדורגת (הופעה רציפה של אלמנטים) מושכת תשומת לב לכל עמודה או נקודה, מה שהופך את הנתונים לקריאים יותר. יישום Canvas מותאם אישית נותן לך פי 3 יותר שליטה על האנימציה בהשוואה לספריות מוכנות, וזה קריטי ללוחות מחוונים מורכבים. יישמנו את זה ב-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, אפקט דומה מושג באמצעות // Каждый столбец анимируется с задержкой 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), ), ); } עם CAKeyframeAnimation — כל keyTimes (עמודה אחת) מקבל CAShapeLayer מוסט:
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)
}תרשים קו מונפש באמצעות 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) } . כדי להוסיף נקודות סמן שמופיעות כשהמברשת עוברת, סנכרן את הופעת CAShapeLayer עם התקדמות strokeEnd: 0 → 1. אנו מיישמים זאת באמצעות CALayer לכל קטע או דרך strokeEnd שעוקב אחר CAAnimationDelegate.animationDidStop.
Tooltip אינטראקטיבי: איך לשמור על FPS
Tooltip שעוקב אחר האצבע על פני הגרף הוא אתגר נפרד. ב-iOS: displayLink → מיפוי קואורדינטה לערך נתונים → presentation().strokeEnd עבור ה-tooltip. חשוב לקרוא ל-UIGestureRecognizer רק על החלק של UIView.animate שמצייר את קו ההדגשה — ציור מחדש של כל ה-Canvas הורג את ה-FPS בזמן תנועת אצבע מהירה. אנו מבטיחים 60fps אפילו עם אינטראקציה אינטנסיבית הודות לרינדור מצטבר.
מה כלול בעבודה שלנו
- קוד מקור של הרכיב המונפש ב-Swift/Kotlin/Dart/TypeScript
- תיעוד להתאמה אישית (צבעים, גופנים, פרמטרי אנימציה)
- אינטגרציה לפרויקט הקיים שלך והגדרת סביבת בדיקה
- אופטימיזציית ביצועים (דגימה מטה עבור סטים >500 נקודות, LOD)
- הבטחת 60fps במכשירים בינוניים
אנו מפתחים אפליקציות מובייל כבר למעלה מ-5 שנים והשלמנו למעלה מ-15 פרויקטים עם גרפיקה מונפשת (פינטק, לוחות מחוונים אנליטיים). ערימת הטכנולוגיות שלנו כוללת: SwiftUI + Combine, Jetpack Compose + Coroutines, Flutter 3.x. ההמלצות של אפל לאנימציה: השתמש ב-CAMediaTiming לסנכרון.
איך ליישם אנימציה מדורגת ב-4 שלבים
- אתחל
setNeedsDisplay()(אוCALayer). - צור מערך של עיכובים:
AnimationController. - עבור כל אלמנט, החל אנימציית
CADisplayLinkעםdelay = index * intervalTimeו-Tween. - התחל את הבקר הראשי — אלמנטים יונפשו ברצף.
מה זה דגימה מטה ולמה צריך אותה?
דגימה מטה (הסרת נקודות) מיושמת בעת רינדור מערכי נתונים גדולים (500+ נקודות) כדי לשמור על 60fps. האלגוריתם [Ramer–Douglas–Peucker](https://ru.wikipedia.org/wiki/Алгоритм_Рамера_—_Дугласа_—_Пекера) מסיר נקודות שנמצאות בתוך אפסילון נתון מקו ישר, תוך שמירה על הצורה החזותית של הגרף.| פלטפורמה | מנגנון אנימציה | מחלקות מפתח |
|---|---|---|
| iOS | Core Animation (CALayer) | CABasicAnimation, CAKeyframeAnimation |
| Android | Canvas + ValueAnimator | ValueAnimator, ObjectAnimator |
| Flutter | AnimationController + Tween | AnimationController, Tween, Interval |
| React Native | Animated API | Animated.Value, Animated.timing |
צור קשר כדי לדון בגרף שלך — נעריך את הפרויקט שלך. הזמן פיתוח אנימציית תרשים סוהר.







