راهنمای سریع انیمیشن ها در نوشتن

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

ویژگی های متداول ترکیب پذیر را متحرک کنید

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

متحرک ظاهر شدن / ناپدید شدن

رنگ سبز خود را نشان می دهد و پنهان می کند
شکل 1. متحرک سازی ظاهر و ناپدید شدن یک آیتم در یک ستون

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

یکی دیگر از گزینه‌های متحرک سازی نمایان شدن یک 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 در نهایت آیتم را از ترکیب حذف می کند.

متحرک سازی آلفای یک آهنگساز
شکل 2. متحرک سازی آلفای یک ترکیب ساز

رنگ پس زمینه را متحرک کنید

قابل ترکیب با تغییر رنگ پس زمینه در طول زمان به عنوان یک انیمیشن، که در آن رنگ ها به یکدیگر محو می شوند.
شکل 3. متحرک کردن رنگ پس زمینه از 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() برای یک تنظیم رنگ یک شات قابل قبول است، اما زمانی که یک رنگ را متحرک می کنید در طول زمان، این می تواند باعث ترکیب مجدد بیش از حد لازم شود.

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

متحرک کردن به اندازه یک ترکیب

متحرک سازی رنگ سبز، اندازه آن به آرامی تغییر می کند.
شکل 4. متحرک سازی هموار بین اندازه کوچک و بزرگتر

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 استفاده کنید تا چگونگی تغییر اندازه را توضیح دهید.

متحرک کردن موقعیت از composable

سبز قابل ترکیب به آرامی متحرک پایین و سمت راست
شکل 5. متحرک با افست قابل ترکیب

برای متحرک کردن موقعیت یک 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
        }
)

اگر می‌خواهید هنگام متحرک کردن موقعیت یا اندازه، مطمئن شوید که Composable‌ها روی یا زیر دیگر composable‌ها کشیده نمی‌شوند، از 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)
    )
}

2 جعبه با جعبه دوم که موقعیت X,Y خود را متحرک می کند، جعبه سوم با حرکت دادن خود به مقدار Y نیز پاسخ می دهد.
شکل 6. متحرک سازی با Modifier.layout{ }

متحرک کردن بالشتک از یک composable

ترکیب سبز با کلیک کوچکتر و بزرگتر می شود و بالشتک متحرک می شود
شکل 7. قابل ترکیب با متحرک سازی آن

برای متحرک سازی padding یک 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
        }
)

متحرک کردن ارتفاع یک ترکیب پذیر

شکل 8. متحرک سازی ارتفاع 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 composable استفاده کنید و ویژگی elevation را روی مقادیر مختلف در هر حالت تنظیم کنید.

مقیاس متن، ترجمه یا چرخش را متحرک کنید

گفتار قابل ساخت متن
شکل 9. متحرک سازی متن به آرامی بین دو اندازه

هنگام متحرک سازی مقیاس، ترجمه یا چرخش متن، پارامتر 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)
    )
}

رنگ متن را متحرک کنید

کلمات
شکل 10. مثالی که رنگ متن متحرک را نشان می دهد

برای متحرک کردن رنگ متن، از 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
    },
    // ...
)

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

صفحه سبز می گوید
شکل 11. استفاده از 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، با لغزش جزئیات قابل ساخت بر روی فرود composable متحرک می‌شود.
شکل 12. متحرک سازی بین اجزای سازنده با استفاده از 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(
            // ...
        )
    }
}

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

یک انیمیشن را تکرار کنید

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

از rememberInfiniteTransition با animationSpec infiniteRepeatable برای تکرار مداوم انیمیشن خود استفاده کنید. RepeatModes تغییر دهید تا مشخص کنید چگونه باید به جلو و عقب برود.

از finiteRepeatable برای تکرار چندین بار استفاده کنید.

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 وارد ترکیب شود. با راه‌اندازی یک کامپوزیشن، یک انیمیشن را شروع می‌کند، می‌توانید از آن برای تغییر حالت انیمیشن استفاده کنید. استفاده از Animatable با متد animateTo برای شروع انیمیشن هنگام راه اندازی:

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

انیمیشن های متوالی ایجاد کنید

چهار دایره با فلش های سبز که بین هر یک متحرک هستند، یکی یکی پس از دیگری متحرک می شوند.
شکل 14. نمودار نحوه پیشرفت یک انیمیشن متوالی را یک به یک نشان می دهد.

از 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))
}

ایجاد انیمیشن های همزمان

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

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

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

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

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

تغییر زمان انیمیشن

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

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

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

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

منابع اضافی

برای نمونه‌های بیشتر از انیمیشن‌های سرگرم‌کننده در Compose، به موارد زیر نگاهی بیندازید:

،

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

ویژگی های متداول ترکیب پذیر را متحرک کنید

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

متحرک ظاهر شدن / ناپدید شدن

رنگ سبز خود را نشان می دهد و پنهان می کند
شکل 1. متحرک سازی ظاهر و ناپدید شدن یک آیتم در یک ستون

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

یکی دیگر از گزینه‌های متحرک سازی نمایان شدن یک 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 در نهایت آیتم را از ترکیب حذف می کند.

متحرک سازی آلفای یک آهنگساز
شکل 2. متحرک سازی آلفای یک ترکیب ساز

رنگ پس زمینه را متحرک کنید

قابل ترکیب با تغییر رنگ پس زمینه در طول زمان به عنوان یک انیمیشن، که در آن رنگ ها به یکدیگر محو می شوند.
شکل 3. متحرک کردن رنگ پس زمینه از 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() برای یک تنظیم رنگ یک شات قابل قبول است، اما زمانی که یک رنگ را متحرک می کنید در طول زمان، این می تواند باعث ترکیب مجدد بیش از حد لازم شود.

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

متحرک کردن به اندازه یک ترکیب

متحرک سازی رنگ سبز، اندازه آن به آرامی تغییر می کند.
شکل 4. متحرک سازی هموار بین اندازه کوچک و بزرگتر

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 استفاده کنید تا چگونگی تغییر اندازه را توضیح دهید.

متحرک کردن موقعیت از composable

سبز قابل ترکیب به آرامی متحرک پایین و سمت راست
شکل 5. متحرک با افست قابل ترکیب

برای متحرک کردن موقعیت یک 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
        }
)

اگر می‌خواهید هنگام متحرک کردن موقعیت یا اندازه، مطمئن شوید که Composable‌ها روی یا زیر دیگر composable‌ها کشیده نمی‌شوند، از 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)
    )
}

2 جعبه با جعبه دوم که موقعیت X,Y خود را متحرک می کند، جعبه سوم با حرکت دادن خود به مقدار Y نیز پاسخ می دهد.
شکل 6. متحرک سازی با Modifier.layout{ }

متحرک کردن بالشتک از یک composable

ترکیب سبز با کلیک کوچکتر و بزرگتر می شود و بالشتک متحرک می شود
شکل 7. قابل ترکیب با متحرک سازی آن

برای متحرک سازی padding یک 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
        }
)

متحرک کردن ارتفاع یک ترکیب پذیر

شکل 8. متحرک سازی ارتفاع 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 composable استفاده کنید و ویژگی elevation را روی مقادیر مختلف در هر حالت تنظیم کنید.

مقیاس متن، ترجمه یا چرخش را متحرک کنید

گفتار قابل ساخت متن
شکل 9. متحرک سازی متن به آرامی بین دو اندازه

هنگام متحرک سازی مقیاس، ترجمه یا چرخش متن، پارامتر 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)
    )
}

رنگ متن را متحرک کنید

کلمات
شکل 10. مثالی که رنگ متن متحرک را نشان می دهد

برای متحرک کردن رنگ متن، از 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
    },
    // ...
)

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

صفحه سبز می گوید
شکل 11. استفاده از 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، با لغزش جزئیات قابل ساخت بر روی فرود composable متحرک می‌شود.
شکل 12. متحرک سازی بین اجزای سازنده با استفاده از 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(
            // ...
        )
    }
}

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

یک انیمیشن را تکرار کنید

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

از rememberInfiniteTransition با animationSpec infiniteRepeatable برای تکرار مداوم انیمیشن خود استفاده کنید. RepeatModes تغییر دهید تا مشخص کنید چگونه باید به جلو و عقب برود.

از finiteRepeatable برای تکرار چندین بار استفاده کنید.

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 وارد ترکیب شود. با راه‌اندازی یک کامپوزیشن، یک انیمیشن را شروع می‌کند، می‌توانید از آن برای تغییر حالت انیمیشن استفاده کنید. استفاده از Animatable با متد animateTo برای شروع انیمیشن هنگام راه اندازی:

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

انیمیشن های متوالی ایجاد کنید

چهار دایره با فلش های سبز که بین هر یک متحرک هستند، یکی یکی پس از دیگری متحرک می شوند.
شکل 14. نمودار نحوه پیشرفت یک انیمیشن متوالی را یک به یک نشان می دهد.

از 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))
}

ایجاد انیمیشن های همزمان

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

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

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

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

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

تغییر زمان انیمیشن

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

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

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

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

منابع اضافی

برای نمونه‌های بیشتر از انیمیشن‌های سرگرم‌کننده در Compose، به موارد زیر نگاهی بیندازید:

،

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

ویژگی های متداول ترکیب پذیر را متحرک کنید

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

متحرک ظاهر شدن / ناپدید شدن

رنگ سبز خود را نشان می دهد و پنهان می کند
شکل 1. متحرک سازی ظاهر و ناپدید شدن یک آیتم در یک ستون

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

یکی دیگر از گزینه‌های متحرک سازی نمایان شدن یک 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 در نهایت آیتم را از ترکیب حذف می کند.

متحرک سازی آلفای یک آهنگساز
شکل 2. متحرک سازی آلفای یک ترکیب ساز

رنگ پس زمینه را متحرک کنید

قابل ترکیب با تغییر رنگ پس زمینه در طول زمان به عنوان یک انیمیشن، که در آن رنگ ها به یکدیگر محو می شوند.
شکل 3. متحرک کردن رنگ پس زمینه از 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() برای یک تنظیم رنگ یک شات قابل قبول است، اما زمانی که یک رنگ را متحرک می کنید در طول زمان، این می تواند باعث ترکیب مجدد بیش از حد لازم شود.

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

متحرک کردن به اندازه یک ترکیب

متحرک سازی رنگ سبز، اندازه آن به آرامی تغییر می کند.
شکل 4. متحرک سازی هموار بین اندازه کوچک و بزرگتر

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 استفاده کنید تا چگونگی تغییر اندازه را توضیح دهید.

متحرک کردن موقعیت از composable

سبز قابل ترکیب به آرامی متحرک پایین و سمت راست
شکل 5. متحرک با افست قابل ترکیب

برای متحرک کردن موقعیت یک 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
        }
)

اگر می‌خواهید هنگام متحرک کردن موقعیت یا اندازه، مطمئن شوید که Composable‌ها روی یا زیر دیگر composable‌ها کشیده نمی‌شوند، از 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)
    )
}

2 جعبه با جعبه دوم که موقعیت X,Y خود را متحرک می کند، جعبه سوم با حرکت دادن خود به مقدار Y نیز پاسخ می دهد.
شکل 6. متحرک سازی با Modifier.layout{ }

متحرک کردن بالشتک از یک composable

ترکیب سبز با کلیک کوچکتر و بزرگتر می شود و بالشتک متحرک می شود
شکل 7. قابل ترکیب با متحرک سازی آن

برای متحرک سازی padding یک 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
        }
)

متحرک کردن ارتفاع یک ترکیب پذیر

شکل 8. متحرک سازی ارتفاع 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 composable استفاده کنید و ویژگی elevation را روی مقادیر مختلف در هر حالت تنظیم کنید.

مقیاس متن، ترجمه یا چرخش را متحرک کنید

گفتار قابل ساخت متن
شکل 9. متحرک سازی متن به آرامی بین دو اندازه

هنگام متحرک سازی مقیاس، ترجمه یا چرخش متن، پارامتر 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)
    )
}

رنگ متن را متحرک کنید

کلمات
شکل 10. مثالی که رنگ متن متحرک را نشان می دهد

برای متحرک کردن رنگ متن، از 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
    },
    // ...
)

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

صفحه سبز می گوید
شکل 11. استفاده از 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، با لغزش جزئیات قابل ساخت بر روی فرود composable متحرک می‌شود.
شکل 12. متحرک سازی بین اجزای سازنده با استفاده از 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(
            // ...
        )
    }
}

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

یک انیمیشن را تکرار کنید

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

از rememberInfiniteTransition با animationSpec infiniteRepeatable برای تکرار مداوم انیمیشن خود استفاده کنید. RepeatModes تغییر دهید تا مشخص کنید چگونه باید به جلو و عقب برود.

از finiteRepeatable برای تکرار چندین بار استفاده کنید.

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 وارد ترکیب شود. با راه‌اندازی یک کامپوزیشن، یک انیمیشن را شروع می‌کند، می‌توانید از آن برای تغییر حالت انیمیشن استفاده کنید. استفاده از Animatable با متد animateTo برای شروع انیمیشن هنگام راه اندازی:

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

انیمیشن های متوالی ایجاد کنید

چهار دایره با فلش های سبز که بین هر یک متحرک هستند، یکی یکی پس از دیگری متحرک می شوند.
شکل 14. نمودار نحوه پیشرفت یک انیمیشن متوالی را یک به یک نشان می دهد.

از 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))
}

ایجاد انیمیشن های همزمان

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

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

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

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

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

زمان انیمیشن را تغییر دهید

آهنگسازی به طور پیش فرض از انیمیشن های بهاری برای اکثر انیمیشن ها استفاده می کند. چشمه ها یا انیمیشن های مبتنی بر فیزیک احساس طبیعی تر می کنند. آنها همچنین به جای زمان ثابت ، سرعت فعلی شی را در نظر می گیرند. اگر می خواهید پیش فرض را نادیده بگیرید ، تمام API های انیمیشن که در بالا نشان داده شده است ، این توانایی را دارند که یک animationSpec تنظیم کنند تا نحوه اجرای یک انیمیشن را تنظیم کند ، خواه بخواهید آن را در مدت زمان مشخصی اجرا کنید یا تند و تیز تر باشید.

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

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

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

منابع اضافی

برای مثال های بیشتر از انیمیشن های سرگرم کننده در آهنگسازی ، به موارد زیر نگاهی بیندازید:

،

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

خصوصیات ترکیب مشترک را تحریک کنید

آهنگسازی API های مناسب را فراهم می کند که به شما امکان می دهد بسیاری از موارد استفاده از انیمیشن مشترک را حل کنید. این بخش نشان می دهد که چگونه می توانید خصوصیات مشترک یک ترکیب را تحریک کنید.

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

نمایشگاه سبز نمایش و مخفی کردن خود
شکل 1. متحرک ظاهر و ناپدید شدن یک مورد در یک ستون

برای مخفی کردن یا نمایش یک ترکیب 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
    // ...
}

پارامترهای Enter and Exit از 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 در نهایت مورد را از ترکیب خارج می کند.

متحرک آلفا از یک ترکیب
شکل 2. انیمیشن آلفا از یک ترکیب

رنگ پس زمینه

ترکیب با رنگ پس زمینه با گذشت زمان به عنوان یک انیمیشن ، جایی که رنگ ها به یکدیگر محو می شوند.
شکل 3. انیمیشن رنگ پس زمینه ترکیب

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

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

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

اندازه یک ترکیب را تحریک کنید

انیمیشن سازگار با ترکیب سبز اندازه آن به آرامی تغییر می کند.
شکل 4. ترکیب یکنواخت بین یک اندازه کوچک و بزرگتر

آهنگسازی به شما امکان می دهد اندازه آهنگسازان را از چند طریق مختلف تحریک کنید. برای انیمیشن های بین تغییرات اندازه ترکیب ، از 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 برای توصیف چگونگی تغییر اندازه.

موقعیت تحریک کننده ترکیب

ترکیب سبز به آرامی انیمیشن و به سمت راست
شکل 5. حرکت سازنده توسط یک جبران

برای تحریک موقعیت یک ترکیب ، از 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)
    )
}

2 جعبه با جعبه 2 که موقعیت X ، Y را متحرک می کند ، جعبه سوم که با حرکت خود با مقدار Y نیز پاسخ می دهد.
شکل 6. انیمیشن با Modifier.layout{ }

تحریم بالشتک یک ترکیب

ترکیب سبز با کلیک کوچکتر و بزرگتر می شود ، با این که بالشتک متحرک است
شکل 7. ترکیب با انیمیشن بالشتک آن

برای تحریک کردن بالشتک یک ترکیب ، از 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
        }
)

ارتفاع یک آهنگسازی

شکل 8. انیمیشن ارتفاع 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 Composive استفاده کنید و خاصیت ارتفاع را بر روی مقادیر مختلف در هر حالت تنظیم کنید.

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

متن ترکیبی متن
شکل 9. متن متحرک هموار بین دو اندازه

هنگام انیمیشن مقیاس ، ترجمه یا چرخش متن ، پارامتر 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)
    )
}

رنگ متن متحرک

کلمات
شکل 10 مثال نشان دادن رنگ متن متحرک

برای تحریک رنگ متن ، از color Lambda در ترکیب 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
    },
    // ...
)

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

صفحه نمایش سبز
شکل 11 با استفاده از AnimatedContent برای تحریک تغییرات بین آهنگ های مختلف (کند شد)

از AnimatedContent برای جابجایی بین آهنگ های مختلف استفاده کنید ، اگر فقط می خواهید یک محو استاندارد بین Composables ، از 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 می توان سفارشی کرد تا انواع مختلفی از انتقال Enter و Exit را نشان دهد. برای اطلاعات بیشتر ، مستندات مربوط به AnimatedContent را بخوانید یا این پست وبلاگ را در AnimatedContent بخوانید.

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

دو آهنگساز ، یکی سبز گفتن و یک جزئیات گفتگوی آبی ، انیمیشن کردن با کشیدن جزئیات مربوط به ترکیبات فرود.
شکل 12. انیمیشن بین آهنگسازان با استفاده از ناوبری

برای تحریک انتقال بین آهنگسازان هنگام استفاده از مصنوعات با استفاده از ناوبری ، 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(
            // ...
        )
    }
}

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

یک انیمیشن را تکرار کنید

پس زمینه سبز که با انیمیشن بین دو رنگ به یک پس زمینه آبی تبدیل می شود.
شکل 13. انیمیشن رنگ پس زمینه بین دو مقدار ، بی نهایت

برای تکرار مداوم animationSpec خود ، از rememberInfiniteTransition infiniteRepeatable استفاده کنید. RepeatModes تغییر دهید تا مشخص شود که چگونه باید به عقب و جلو برود.

برای تکرار تعداد مشخصی بار finiteRepeatable استفاده کنید.

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
    }
)

انیمیشن های متوالی ایجاد کنید

چهار دایره با فلش های سبز که بین هر یک متحرک هستند ، یک به یک بعد از یکدیگر متحرک می شوند.
شکل 14. نمودار نشان می دهد که چگونه یک انیمیشن پی در پی پیشرفت می کند ، یک به یک.

برای اجرای انیمیشن های متوالی یا همزمان از API های Coroutine 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))
}

انیمیشن های همزمان ایجاد کنید

سه دایره با فلش های سبز که به هر یک متحرک می شوند ، همه را همزمان با هم متحرک می کنند.
شکل 15. نمودار نشان می دهد که چگونه انیمیشن های همزمان پیشرفت می کنند ، همه در همان زمان.

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

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

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

شما می توانید از API 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
    }
}

عملکرد انیمیشن را بهینه کنید

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

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

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

زمان انیمیشن را تغییر دهید

آهنگسازی به طور پیش فرض از انیمیشن های بهاری برای اکثر انیمیشن ها استفاده می کند. چشمه ها یا انیمیشن های مبتنی بر فیزیک احساس طبیعی تر می کنند. آنها همچنین به جای زمان ثابت ، سرعت فعلی شی را در نظر می گیرند. اگر می خواهید پیش فرض را نادیده بگیرید ، تمام API های انیمیشن که در بالا نشان داده شده است ، این توانایی را دارند که یک animationSpec تنظیم کنند تا نحوه اجرای یک انیمیشن را تنظیم کند ، خواه بخواهید آن را در مدت زمان مشخصی اجرا کنید یا تند و تیز تر باشید.

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

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

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

منابع اضافی

برای مثال های بیشتر از انیمیشن های سرگرم کننده در آهنگسازی ، به موارد زیر نگاهی بیندازید: