راهنمای سریع پویانمایی‌ها در Compose

‫Compose سازوکارهای پویانمایی داخلی زیادی دارد و ممکن است انتخاب اینکه از کدام استفاده کنید گیج‌کننده باشد. در زیر فهرستی از موارد استفاده رایج از پویانمایی آمده است. برای کسب اطلاعات دقیق‌تر درباره مجموعه کامل گزینه‌های مختلف API دسترسی‌پذیر برای شما، مستندات کامل «ساختن پویانمایی» را بخوانید.

پویانمایی کردن دارایی‌های ترکیبی مشترک

‫Compose میاناهای برنامه‌سازی کاربردی مناسبی ارائه می‌دهد که به شما امکان می‌دهد بسیاری از موارد استفاده رایج پویانمایی را حل کنید. این بخش نشان می‌دهد چگونه می‌توانید ویژگی‌های معمول یک عنصر ترکیبی را پویانمایی کنید.

پویانمایی ظاهر شدن / ناپدید شدن

عنصر ترکیبی سبز که خود را نشان می‌دهد و پنهان می‌کند
شکل ۱. پویانمایی ظاهر و ناپدید شدن موردی در ستون

از AnimatedVisibility برای پنهان کردن یا نشان دادن «ترکیبی» استفاده کنید. کودکان داخل AnimatedVisibility می‌توانند از Modifier.animateEnterExit() برای گذار ورود یا خروج خود استفاده کنند.

var visible by remember {
    mutableStateOf(true)
}
// Animated visibility will eventually remove the item from the composition once the animation has finished.
AnimatedVisibility(visible) {
    // your composable here
    // ...
}

پارامترهای ورود و خروج AnimatedVisibility به شما امکان می‌دهند نحوه عملکرد یک عنصر ترکیبی را هنگام ظاهر و ناپدید شدن پیکربندی کنید. برای کسب اطلاعات بیشتر، اسناد کامل را بخوانید.

گزینه دیگر برای پویانمایی کردن نمایان بودن یک عنصر ترکیبی، پویانمایی کردن آلفا در طول زمان بااستفاده از animateFloatAsState است:

var visible by remember {
    mutableStateOf(true)
}
val animatedAlpha by animateFloatAsState(
    targetValue = if (visible) 1.0f else 0f,
    label = "alpha"
)
Box(
    modifier = Modifier
        .size(200.dp)
        .graphicsLayer {
            alpha = animatedAlpha
        }
        .clip(RoundedCornerShape(8.dp))
        .background(colorGreen)
        .align(Alignment.TopCenter)
) {
}

بااین‌حال، تغییر دادن آلفا با این هشدار همراه است که عنصر ترکیبی در ترکیب باقی می‌ماند و همچنان فضایی را که در آن چیده شده است اشغال می‌کند. این کار باعث می‌شود صفحه‌خوان‌ها و دیگر سازوکارهای دسترس‌پذیری همچنان مورد را روی صفحه درنظر بگیرند. از طرف دیگر، AnimatedVisibility درنهایت مورد را از ترکیب برمی‌دارد.

پویانمایی آلفای یک عنصر ترکیبی
شکل ۲. درحال پویانمایی کردن آلفای یک عنصر ترکیبی

پویانمایی رنگ پس‌زمینه

ترکیب‌شدنی با تغییر رنگ پس‌زمینه درطول زمان به‌عنوان پویانمایی، جایی که رنگ‌ها در یکدیگر محو می‌شوند.
شکل ۳. درحال پویانمایی رنگ پس‌زمینه عنصر ترکیبی

val animatedColor by animateColorAsState(
    if (animateBackgroundColor) colorGreen else colorBlue,
    label = "color"
)
Column(
    modifier = Modifier.drawBehind {
        drawRect(animatedColor)
    }
) {
    // your composable here
}

این گزینه عملکرد بهتری نسبت‌به استفاده از Modifier.background() دارد. ‫Modifier.background() برای تنظیم رنگ تک‌ضرب قابل‌قبول است، اما وقتی رنگی را درطول زمان پویانمایی می‌کنید، این کار می‌تواند باعث شود ترکیب‌های بیشتری از حد لازم انجام شود.

برای پویانمایی بی‌نهایت رنگ پس‌زمینه، به تکرار بخش پویانمایی مراجعه کنید.

اندازه عنصر ترکیبی را پویانمایی کنید

عنصر ترکیبی سبز که تغییر اندازه آن به‌آرامی متحرک می‌شود.
شکل ۴. عنصر ترکیبی که به‌نرمی بین اندازه کوچک و بزرگ‌تر پویانمایی می‌کند

‫Compose به شما امکان می‌دهد اندازه عناصر ترکیبی را به چند روش مختلف پویانمایی کنید. از animateContentSize() برای پویانمایی‌های بین تغییرات اندازه ترکیب‌شدنی استفاده کنید.

برای مثال، اگر چارگوشی دارید که حاوی نوشتاری است که می‌تواند از یک خط به چند خط گسترش یابد، می‌توانید از Modifier.animateContentSize() برای دستیابی به انتقالی هموارتر استفاده کنید:

var expanded by remember { mutableStateOf(false) }
Box(
    modifier = Modifier
        .background(colorBlue)
        .animateContentSize()
        .height(if (expanded) 400.dp else 200.dp)
        .fillMaxWidth()
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            expanded = !expanded
        }

) {
}

همچنین می‌توانید از AnimatedContent با SizeTransform برای توصیف نحوه تغییر اندازه استفاده کنید.

پویانمایی موقعیت عنصر ترکیبی

عنصر ترکیبی سبز به‌آرامی به پایین و راست پویانمایی می‌شود
شکل ۵. ترکیب‌شدنی با حرکت براساس انحراف

برای پویانمایی کردن موقعیت یک عنصر ترکیبی، از Modifier.offset{ } همراه با animateIntOffsetAsState() استفاده کنید.

var moved by remember { mutableStateOf(false) }
val pxToMove = with(LocalDensity.current) {
    100.dp.toPx().roundToInt()
}
val offset by animateIntOffsetAsState(
    targetValue = if (moved) {
        IntOffset(pxToMove, pxToMove)
    } else {
        IntOffset.Zero
    },
    label = "offset"
)

Box(
    modifier = Modifier
        .offset {
            offset
        }
        .background(colorBlue)
        .size(100.dp)
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            moved = !moved
        }
)

اگر می‌خواهید مطمئن شوید که عناصر ترکیبی هنگام پویانمایی موقعیت یا اندازه روی یا زیر عناصر ترکیبی دیگر رسم نمی‌شوند، از Modifier.layout{ } استفاده کنید. این اصلاح‌کننده تغییرات اندازه و موقعیت را به والد منتقل می‌کند، که سپس بر فرزندان دیگر تأثیر می‌گذارد.

برای مثال، اگر Box را در Column جابه‌جا می‌کنید و سایر کودکان باید هنگام جابه‌جایی Box جابه‌جا شوند، اطلاعات جبران را با Modifier.layout{ } به‌صورت زیر اضافه کنید:

var toggled by remember {
    mutableStateOf(false)
}
val interactionSource = remember {
    MutableInteractionSource()
}
Column(
    modifier = Modifier
        .padding(16.dp)
        .fillMaxSize()
        .clickable(indication = null, interactionSource = interactionSource) {
            toggled = !toggled
        }
) {
    val offsetTarget = if (toggled) {
        IntOffset(150, 150)
    } else {
        IntOffset.Zero
    }
    val offset = animateIntOffsetAsState(
        targetValue = offsetTarget, label = "offset"
    )
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(colorBlue)
    )
    Box(
        modifier = Modifier
            .layout { measurable, constraints ->
                val offsetValue = if (isLookingAhead) offsetTarget else offset.value
                val placeable = measurable.measure(constraints)
                layout(placeable.width + offsetValue.x, placeable.height + offsetValue.y) {
                    placeable.placeRelative(offsetValue)
                }
            }
            .size(100.dp)
            .background(colorGreen)
    )
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(colorBlue)
    )
}

‫۲ چارگوش که چارگوش دوم موقعیت X و Y خود را پویانمایی می‌کند، چارگوش سوم با حرکت دادن خود به میزان Y پاسخ می‌دهد.
شکل ۶. درحال پویانمایی با Modifier.layout{ }

پویانمایی حاشیه داخلی عنصر ترکیبی

عنصر ترکیبی سبز با کلیک کوچک و بزرگ می‌شود و حاشیه آن متحرک می‌شود
شکل ۷. ترکیب‌شدنی با پدینگ پویانمایی‌شده

برای پویانمایی کردن حاشیه یک عنصر ترکیبی، از animateDpAsState همراه با Modifier.padding() استفاده کنید:

var toggled by remember {
    mutableStateOf(false)
}
val animatedPadding by animateDpAsState(
    if (toggled) {
        0.dp
    } else {
        20.dp
    },
    label = "padding"
)
Box(
    modifier = Modifier
        .aspectRatio(1f)
        .fillMaxSize()
        .padding(animatedPadding)
        .background(Color(0xff53D9A1))
        .clickable(
            interactionSource = remember { MutableInteractionSource() },
            indication = null
        ) {
            toggled = !toggled
        }
)

پویانمایی ارتفاع عنصر ترکیبی

شکل ۸. ارتفاع عنصر ترکیبی با کلیک پویانمایی می‌شود

برای پویانمایی ارتفاع یک عنصر ترکیبی، از animateDpAsState همراه با Modifier.graphicsLayer{ } استفاده کنید. برای تغییرات ارتفاع یک‌باره، از Modifier.shadow() استفاده کنید. اگر سایه را پویانمایی می‌کنید، استفاده از اصلاح‌گر Modifier.graphicsLayer{ } گزینه کارآمدتری است.

val mutableInteractionSource = remember {
    MutableInteractionSource()
}
val pressed = mutableInteractionSource.collectIsPressedAsState()
val elevation = animateDpAsState(
    targetValue = if (pressed.value) {
        32.dp
    } else {
        8.dp
    },
    label = "elevation"
)
Box(
    modifier = Modifier
        .size(100.dp)
        .align(Alignment.Center)
        .graphicsLayer {
            this.shadowElevation = elevation.value.toPx()
        }
        .clickable(interactionSource = mutableInteractionSource, indication = null) {
        }
        .background(colorGreen)
) {
}

یا از عنصر ترکیبی Card استفاده کنید و دارایی ارتفاع را روی مقادیر مختلف برای هر وضعیت تنظیم کنید.

پویاسازی مقیاس نوشتار، ترجمه، یا چرخش

عنصر ترکیبی نوشتاری که می‌گوید «سلام» و بین اندازه کوچک و بزرگ پویانمایی می‌شود.
شکل ۹. نوشتار به‌آرامی بین دو اندازه پویانمایی می‌شود

هنگام پویانمایی مقیاس، انتقال، یا چرخش نوشتار، textMotion پارامتر را در TextStyle روی TextMotion.Animated تنظیم کنید. این کار باعث می‌شود گذار بین پویانمایی‌های نوشتاری روان‌تر باشد. از Modifier.graphicsLayer{ } برای ترجمه، چرخش، یا مقیاس‌بندی نوشتار استفاده کنید.

val infiniteTransition = rememberInfiniteTransition(label = "infinite transition")
val scale by infiniteTransition.animateFloat(
    initialValue = 1f,
    targetValue = 8f,
    animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse),
    label = "scale"
)
Box(modifier = Modifier.fillMaxSize()) {
    Text(
        text = "Hello",
        modifier = Modifier
            .graphicsLayer {
                scaleX = scale
                scaleY = scale
                transformOrigin = TransformOrigin.Center
            }
            .align(Alignment.Center),
        // Text composable does not take TextMotion as a parameter.
        // Provide it via style argument but make sure that we are copying from current theme
        style = LocalTextStyle.current.copy(textMotion = TextMotion.Animated)
    )
}

پویانمایی رنگ نوشتار

کلمه «سلام» درحال پویانمایی رنگ خود بین سبز و آبی در «نوشتن سلام»
شکل ۱۰. نمونه‌ای که رنگ نوشتار پویانمایی‌شده را نشان می‌دهد

برای پویانمایی رنگ نوشتار، از color لامبدا در BasicText ترکیب‌شدنی استفاده کنید:

val infiniteTransition = rememberInfiniteTransition(label = "infinite transition")
val animatedColor by infiniteTransition.animateColor(
    initialValue = Color(0xFF60DDAD),
    targetValue = Color(0xFF4285F4),
    animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse),
    label = "color"
)

BasicText(
    text = "Hello Compose",
    color = {
        animatedColor
    },
    // ...
)

جابه‌جا شدن بین انواع مختلف محتوا

صفحه سبز با نوشته «درحال بار کردن»، صفحه آبی با نوشته «بارگیری شد»، و صفحه سفید با نوشته «خطا»، ترکیب‌پذیرهای مختلف با پویانمایی ساده درحال تکرار شدن هستند.
شکل ۱۱. استفاده از AnimatedContent برای پویانمایی تغییرات بین عناصر ترکیبی مختلف (آهسته شده)

اگر فقط محو شدن استاندارد بین عناصر ترکیبی را می‌خواهید، از Crossfade استفاده کنید. برای پویانمایی بین عناصر ترکیبی مختلف، از AnimatedContent استفاده کنید.

var state by remember {
    mutableStateOf(UiState.Loading)
}
AnimatedContent(
    state,
    transitionSpec = {
        fadeIn(
            animationSpec = tween(3000)
        ) togetherWith fadeOut(animationSpec = tween(3000))
    },
    modifier = Modifier.clickable(
        interactionSource = remember { MutableInteractionSource() },
        indication = null
    ) {
        state = when (state) {
            UiState.Loading -> UiState.Loaded
            UiState.Loaded -> UiState.Error
            UiState.Error -> UiState.Loading
        }
    },
    label = "Animated Content"
) { targetState ->
    when (targetState) {
        UiState.Loading -> {
            LoadingScreen()
        }
        UiState.Loaded -> {
            LoadedScreen()
        }
        UiState.Error -> {
            ErrorScreen()
        }
    }
}

‫AnimatedContent را می‌توان سفارشی‌سازی کرد تا انواع مختلفی از گذرهای ورود و خروج را نمایش دهد. برای اطلاعات بیشتر، مستندات مربوط به AnimatedContent را بخوانید یا این پست وبلاگ درباره AnimatedContent را بخوانید.

هنگام ناوبری به مقصدهای مختلف، پویانمایی کنید

دو عنصر ترکیبی، یکی سبز با عنوان «صفحه مقصد» و دیگری آبی با عنوان «جزئیات»، که با لغزاندن عنصر ترکیبی جزئیات روی عنصر ترکیبی صفحه مقصد پویانمایی می‌شوند.
شکل ۱۲. پویانمایی بین عناصر ترکیبی بااستفاده از navigation-compose

برای پویانمایی کردن گذارها بین عناصر ترکیبی هنگام استفاده از navigation-compose عنصر، enterTransition و exitTransition را در عنصر ترکیبی مشخص کنید. همچنین می‌توانید پویانمایی پیش‌فرض را تنظیم کنید تا برای همه مقصدها در سطح بالا NavHost استفاده شود:

val navController = rememberNavController()
NavHost(
    navController = navController, startDestination = "landing",
    enterTransition = { EnterTransition.None },
    exitTransition = { ExitTransition.None }
) {
    composable("landing") {
        ScreenLanding(
            // ...
        )
    }
    composable(
        "detail/{photoUrl}",
        arguments = listOf(navArgument("photoUrl") { type = NavType.StringType }),
        enterTransition = {
            fadeIn(
                animationSpec = tween(
                    300, easing = LinearEasing
                )
            ) + slideIntoContainer(
                animationSpec = tween(300, easing = EaseIn),
                towards = AnimatedContentTransitionScope.SlideDirection.Start
            )
        },
        exitTransition = {
            fadeOut(
                animationSpec = tween(
                    300, easing = LinearEasing
                )
            ) + slideOutOfContainer(
                animationSpec = tween(300, easing = EaseOut),
                towards = AnimatedContentTransitionScope.SlideDirection.End
            )
        }
    ) { backStackEntry ->
        ScreenDetails(
            // ...
        )
    }
}

انواع مختلفی از انتقال‌های ورود و خروج وجود دارد که جلوه‌های متفاوتی را بر محتوای ورودی و خروجی اعمال می‌کنند، برای اطلاعات بیشتر به مستندات مراجعه کنید.

تکرار پویانمایی

پس‌زمینه سبز که با پویانمایی بین دو رنگ، بی‌نهایت به پس‌زمینه آبی تبدیل می‌شود.
شکل ۱۳. رنگ پس‌زمینه به‌طور نامحدود بین دو مقدار پویانمایی می‌شود

از rememberInfiniteTransition با infiniteRepeatable animationSpec برای تکرار مداوم پویانمایی استفاده کنید. RepeatModes را تغییر دهید تا نحوه رفت‌وبرگشت آن را مشخص کنید.

از repeatable برای تکرار تعداد معینی از دفعات استفاده کنید.

val infiniteTransition = rememberInfiniteTransition(label = "infinite")
val color by infiniteTransition.animateColor(
    initialValue = Color.Green,
    targetValue = Color.Blue,
    animationSpec = infiniteRepeatable(
        animation = tween(1000, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    ),
    label = "color"
)
Column(
    modifier = Modifier.drawBehind {
        drawRect(color)
    }
) {
    // your composable here
}

شروع پویانمایی در زمان راه‌اندازی عنصر ترکیبی

LaunchedEffect وقتی اجرا می‌شود که عنصر ترکیبی وارد ترکیب‌بندی شود. با راه‌اندازی عنصر ترکیبی، پویانمایی شروع می‌شود، می‌توانید از این برای هدایت تغییر وضعیت پویانمایی استفاده کنید. استفاده از Animatable با روش animateTo برای شروع پویانمایی در زمان راه‌اندازی:

val alphaAnimation = remember {
    Animatable(0f)
}
LaunchedEffect(Unit) {
    alphaAnimation.animateTo(1f)
}
Box(
    modifier = Modifier.graphicsLayer {
        alpha = alphaAnimation.value
    }
)

ساختن پویانمایی‌های ترتیبی

چهار دایره با پیکان‌های سبز که بین هرکدام پویانمایی می‌کنند، یکی پس‌از دیگری پویانمایی می‌شوند.
شکل ۱۴. نموداری که نشان می‌دهد پویانمایی ترتیبی چگونه یکی‌یکی پیش می‌رود.

از APIهای روتین فرعی Animatable برای اجرای پویانمایی‌های متوالی یا هم‌زمان استفاده کنید. تماس با animateTo در Animatable یکی پس‌از دیگری باعث می‌شود هر پویانمایی منتظر بماند تا پویانمایی‌های قبلی تمام شوند و سپس ادامه دهد. این به‌دلیل عملکرد تعلیق است.

val alphaAnimation = remember { Animatable(0f) }
val yAnimation = remember { Animatable(0f) }

LaunchedEffect("animationKey") {
    alphaAnimation.animateTo(1f)
    yAnimation.animateTo(100f)
    yAnimation.animateTo(500f, animationSpec = tween(100))
}

ساختن پویانمایی‌های هم‌زمان

سه دایره با پیکان‌های سبز که به‌سمت هریک از آن‌ها حرکت می‌کنند و همه با هم به‌طور هم‌زمان پویانمایی می‌شوند.
شکل ۱۵. نموداری که نشان می‌دهد پویانمایی‌های هم‌زمان چگونه پیش می‌روند، همه در یک زمان.

برای دستیابی به پویانمایی‌های هم‌زمان، از میاناهای برنامه‌سازی کاربردی روتین همکار (Animatable#animateTo() یا animate) یا از میانای برنامه‌سازی کاربردی Transition استفاده کنید. اگر از چندین تابع راه‌اندازی در زمینه روتین همکار استفاده کنید، پویانمایی‌ها را به‌طور هم‌زمان راه‌اندازی می‌کند:

val alphaAnimation = remember { Animatable(0f) }
val yAnimation = remember { Animatable(0f) }

LaunchedEffect("animationKey") {
    launch {
        alphaAnimation.animateTo(1f)
    }
    launch {
        yAnimation.animateTo(100f)
    }
}

می‌توانید از updateTransition API برای استفاده از وضعیت یکسان برای هدایت هم‌زمان بسیاری از پویانمایی‌های مختلف دارایی استفاده کنید. مثال زیر دو دارایی را که با تغییر وضعیت کنترل می‌شوند، rect و borderWidth، پویانمایی می‌کند:

var currentState by remember { mutableStateOf(BoxState.Collapsed) }
val transition = updateTransition(currentState, label = "transition")

val rect by transition.animateRect(label = "rect") { state ->
    when (state) {
        BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f)
        BoxState.Expanded -> Rect(100f, 100f, 300f, 300f)
    }
}
val borderWidth by transition.animateDp(label = "borderWidth") { state ->
    when (state) {
        BoxState.Collapsed -> 1.dp
        BoxState.Expanded -> 0.dp
    }
}

بهینه‌سازی عملکرد پویانمایی

پویانمایی‌ها در Compose می‌توانند باعث مشکلات عملکرد شوند. این امر به‌دلیل ماهیت پویانمایی است: پویانمایی عبارت است از حرکت یا تغییر سریع پیکسل‌ها روی صفحه، قاب‌به‌قاب، برای ایجاد توهم حرکت.

مراحل مختلف «ترکیب» را درنظر بگیرید: ترکیب، چیدمان، و طراحی. اگر پویانمایی شما فاز چیدمان را تغییر دهد، همه عناصر ترکیبی تحت‌تأثیر قرارگرفته باید دوباره چیده و دوباره رسم شوند. اگر پویانمایی شما در مرحله رسم رخ دهد، به‌طور پیش‌فرض عملکرد بهتری نسبت‌به زمانی دارد که پویانمایی را در مرحله چیدمان اجرا کنید، زیرا در این حالت کار کمتری برای انجام دادن دارد.

برای اینکه مطمئن شوید برنامه شما درحین پویانمایی تا حد امکان کار کمی انجام می‌دهد، درصورت امکان نسخه لامبدای Modifier را انتخاب کنید. این کار باعث می‌شود بازآرایی رد شود و پویانمایی خارج از مرحله ترکیب انجام شود، درغیراین‌صورت از Modifier.graphicsLayer{ } استفاده کنید، زیرا این اصلاح‌گر همیشه در مرحله کشیدن اجرا می‌شود. برای کسب اطلاعات بیشتر درباره این موضوع، به بخش به‌تعویق انداختن خواندن‌ها در اسناد عملکرد مراجعه کنید.

تغییر زمان‌بندی پویانمایی

‫Compose به‌طور پیش‌فرض از پویانمایی‌های فنری برای اکثر پویانمایی‌ها استفاده می‌کند. فنرها یا پویانمایی‌های مبتنی بر فیزیک، طبیعی‌تر به‌نظر می‌رسند. این حرکت‌ها همچنین قابل‌وقفه هستند زیرا به‌جای زمان ثابت، سرعت فعلی شیء را درنظر می‌گیرند. اگر می‌خواهید پیش‌فرض را ملغی کنید، همه APIهای پویانمایی که قبلاً نشان داده شده‌اند قابلیت تنظیم animationSpec را دارند تا نحوه اجرای پویانمایی را سفارشی‌سازی کنند، چه بخواهید پویانمایی در مدت زمان معینی اجرا شود یا جهشی‌تر باشد.

در زیر خلاصه‌ای از گزینه‌های مختلف animationSpec آمده است:

  • spring: پویانمایی مبتنی بر فیزیک، پیش‌فرض برای همه پویانمایی‌ها. می‌توانید سختی یا نسبت میرایی را تغییر دهید تا به پویانمایی با ظاهر و احساسی متفاوت دست یابید.
  • tween (کوته‌نوشت بین): پویانمایی مبتنی بر مدت، بین دو مقدار با تابع Easing پویانمایی می‌کند.
  • keyframes: مشخصات برای تعیین مقادیر در نقاط کلیدی خاص در یک پویانمایی.
  • repeatable: مشخصات مبتنی بر مدت که تعداد مشخصی بار اجرا می‌شود، که توسط RepeatMode مشخص شده است.
  • infiniteRepeatable: مشخصات مبتنی بر مدت که برای همیشه اجرا می‌شود.
  • snap: بدون هیچ پویانمایی، فوراً به مقدار نهایی می‌چسبد.
دو پویانمایی که مجموعه مشخصات بدون مشخصات دربرابر مجموعه مشخصات سفارشی «بهار» را نشان می‌دهد.
شکل ۱۶. مشخصات تنظیم‌شده دربرابر مشخصات «چشمه داغ سفارشی» تنظیم نشده است

برای اطلاعات بیشتر درباره animationSpecs، مستندات کامل را بخوانید.

منابع بیشتر

برای دیدن نمونه‌های بیشتر از پویانمایی‌های جالب در «نوشتن»، به موارد زیر نگاهی بیندازید: