راهنمای سریع پویانمایی‌ها در 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 moving by an offset

برای پویانمایی کردن موقعیت یک 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 هنگام کلیک

برای پویانمایی کردن ارتفاع یک 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 را بخوانید.

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

دو ترکیب‌پذیر، یکی سبز با عنوان Landing و دیگری آبی با عنوان Detail، با لغزاندن ترکیب‌پذیر detail روی ترکیب‌پذیر landing پویانمایی می‌شوند.
شکل ۱۲. پویانمایی بین 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 استفاده کنید تا پویانمایی‌تان به‌طور مداوم تکرار شود. RepeatMode را تغییر دهید تا مشخص کنید چطور باید عقب و جلو برود.

از 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 یکی پس‌از دیگری باعث می‌شود هر پویانمایی منتظر بماند تا پویانمایی‌های قبلی تمام شوند و سپس ادامه دهد. دلیلش این است که این تابع از نوع suspend است.

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: فوراً بدون هیچ پویانمایی‌ای به مقدار نهایی می‌رود.
دو پویانمایی که نشان می‌دهد هیچ مشخصه‌ای تنظیم نشده است درمقابل یک مجموعه مشخصه «فنری» سفارشی.
شکل ۱۶. بدون مجموعه مشخصات درمقابل مجموعه مشخصات «چشمه داغ سفارشی»

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

منابع بیشتر

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