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

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

پویانمایی کردن ویژگی‌های ترکیب‌پذیر رایج

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

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

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

از AnimatedVisibility برای پنهان کردن یا نمایش دادن Composable استفاده کنید. فرزندان داخل 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 به شما امکان می‌دهد نحوه رفتار یک ترکیب‌پذیر را هنگام ظاهر شدن و ناپدید شدن پیکربندی کنید. برای اطلاعات بیشتر، سند کامل را بخوانید.

گزینه دیگر برای پویانمایی کردن نمایان بودن یک composable، پویانمایی کردن آلفا درطول زمان بااستفاده از 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 درنهایت مورد را از ترکیب‌بندی برمی‌دارد.

پویانمایی کردن آلفای یک ترکیب‌پذیر
شکل ۲. پویانمایی کردن آلفای یک composable

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

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

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

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

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

Animate the size of a composable

ترکیب‌پذیر سبز که تغییر اندازه خود را به‌صورت روان پویانمایی می‌کند.
شکل ۴. Composable smoothly animating between a small and a larger size

Compose به شما امکان می‌دهد اندازه composable را به چند روش مختلف پویانمایی کنید. از 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 برای توصیف نحوه تغییر اندازه استفاده کنید.

پویانمایی کردن موقعیت ترکیب‌پذیر

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

برای پویانمایی کردن موقعیت یک ترکیب‌پذیر، از 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{ }

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

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

برای پویانمایی کردن حاشیه داخلی یک ترکیب‌شدنی، از 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
        }
)

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

شکل ۸. پویانمایی ارتفاع Composable هنگام کلیک

برای پویانمایی کردن ارتفاع یک ترکیب‌پذیر، از 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)
    )
}

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

کلمات Hello Compose که رنگ آن بین سبز و آبی پویانمایی می‌شود
شکل ۱۰. نمونه‌ای که رنگ نوشتار پویانمایی را نشان می‌دهد

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

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 برای پویانمایی تغییرات بین ترکیب‌پذیرهای مختلف (آهسته شده)

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

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 را بخوانید.

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

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

برای پویانمایی انتقال‌ها بین composable هنگام استفاده از ساخته navigation-compose،‏ enterTransition و exitTransition را در composable مشخص کنید. همچنین می‌توانید پویانمایی پیش‌فرض را برای استفاده در همه مقصدهای سطح بالا 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
}

شروع پویانمایی هنگام راه‌اندازی یک composable

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

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

ایجاد پویانمایی‌های متوالی

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

از میاناهای برنامه‌سازی کاربردی هم‌روال 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))
}

ایجاد پویانمایی‌های هم‌زمان

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

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

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

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

می‌توانید از میانای برنامه‌سازی کاربردی updateTransition استفاده کنید تا از همان حالت برای هدایت هم‌زمان بسیاری از پویانمایی‌های ویژگی مختلف استفاده کنید. مثال زیر دو ویژگی را که با تغییر وضعیت کنترل می‌شوند پویانمایی می‌کند، 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 می‌توانند باعث بروز مشکلات عملکردی شوند. این به‌دلیل ماهیت پویانمایی است: حرکت یا تغییر سریع پیکسل‌ها در صفحه‌نمایش، فریم‌به‌فریم برای ایجاد توهم حرکت.

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

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

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

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

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

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

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

منابع بیشتر

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