نکات اصلی
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 میلیثانیه است.
در 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 و callbackهای startAnimation/pauseAnimation/stopAnimation انتزاع میکند. در SwiftUI، ساختار Animation duration را بهعنوان بخشی از مشخصات انیمیشن ارائه میدهد، اما مدیریت مستقیم خط زمانی پنهان است — برای timeline سفارشی از TimelineView (iOS 15+) با scheduler خود استفاده کنید.
در 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 باشد — مدت نادرست میتواند باعث افت فریم به دلیل عدم تطابق با نرخ تازهسازی صفحه شود.
خط زمانی بهویژه در ترکیب چندین انیمیشن اهمیت پیدا میکند. سه الگوی پایه: آبشاری — انیمیشنها به صورت ترتیبی با جابجایی راهاندازی میشوند (هر کدام بعدی پس از offset از قبلی شروع میشود)، موازی — همه انیمیشنها همزمان با timeline یکسان راهاندازی میشوند و stagger — انیمیشنها با همپوشانی راهاندازی میشوند (دومی قبل از پایان اولی شروع میشود).
در iOS، آبشار از طریق beginTime هر CAAnimation = beginTime قبلی + duration قبلی پیادهسازی میشود. برای stagger از CAAnimationGroup با beginTimeهای مختلف در انیمیشنهای تو در تو استفاده میشود. در Android، AnimatorSet از playSequentially و playTogether پشتیبانی میکند؛ stagger با تنظیم startDelay برای هر Animator ایجاد میشود. در Compose، AnimatedVisibility با enter/exitTransition از stagger در variant staggerChildren برای ظاهر شدن آبشاری عناصر لیست استفاده میکند. در WWDC و Google I/O 2024، هر دو پلتفرم ابزارهای تجسم timeline را معرفی کردند: Xcode Animations Inspector و Android Studio Animations Timeline — آنها خط زمانی واقعی و فریمهای افتاده را در زمان واقعی نشان میدهند.
مثال انیمیشن با timeline سفارشی: تأخیر 0.3 ثانیه، مدت 0.6 ثانیه، autoreverse و تکرار بینهایت. CAMediaTiming تمام پارامترهای زمانی را مدیریت میکند.
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.0 دو برابر سریع کنید.
SwiftUI TimelineView (iOS 15+) به سیستم timeline برای انیمیشن سفارشی دسترسی میدهد. نما در هر تیک scheduler بهروز میشود.
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 است.
ValueAnimator با مدیریت کامل خط زمانی: تأخیر، مدت، تکرار. متد setCurrentPlayTime امکان پرش روی خط زمانی را فراهم میکند.
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() استفاده کنید.
AnimatorSet امکان ایجاد خطوط زمانی پیچیده از چندین انیمیشن را فراهم میکند. در مثال — اجرای ترتیبی با همپوشانی (stagger).
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 ms)، slideUp بعد از 100 ms، scale بعد از 200 ms. خط زمانی حاصل: 0 — 100 — 200 — 600 ms. برای اجرای ترتیبی از playSequentially استفاده کنید.
سوالات متداول
Animator — کلاس پایه انتزاعی برای تمام انیمیشنها است. ValueAnimator — زیرکلاس آن است که مقادیر عددی را بدون وابستگی به شیء انیمیشن میکند. ObjectAnimator (وارث ValueAnimator) ویژگی مشخصی از شیء را انیمیشن میکند (مثلاً alpha یا translationY). برای timeline از ValueAnimator بهعنوان تایمر جهانی با callback از طریق addUpdateListener استفاده کنید.
برای محاسبه پیشرفت از ساعتهای سیستمی (CACurrentMediaTime در iOS، System.nanoTime در Android) استفاده کنید، نه تعداد فریمها. برای همگامسازی شبکهای از تایمر مبتنی بر NTP با تصحیح offset استفاده کنید. iOS از AVAudioSession برای همگامسازی timeline صوتی-تصویری با دقت میلیثانیه پشتیبانی میکند. از همگامسازی بر اساس فریم خودداری کنید — نرخ فریم از 30 تا 120 fps متغیر است.
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 برای کش کردن ترسیمهای پیچیده استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید