Compose-এ অনেক বিল্ট-ইন অ্যানিমেশন মেকানিজম আছে এবং কোনটি বেছে নিতে হবে তা বোঝা কঠিন হতে পারে। নিচে অ্যানিমেশন ব্যবহারের সাধারণ কেসগুলির একটি তালিকা দেওয়া হল। আপনার জন্য উপলভ্য বিভিন্ন API বিকল্পের সম্পূর্ণ সেট সম্পর্কে আরও বিস্তারিত তথ্য পেতে, সম্পূর্ণ কম্পোজ অ্যানিমেশন ডকুমেন্টেশন পড়ুন।
সাধারণ কম্পোজ করার উপযুক্ত প্রপার্টি অ্যানিমেট করা
Compose এমন API প্রদান করে যা আপনাকে অনেক সাধারণ অ্যানিমেশন ব্যবহারের ক্ষেত্রে সমাধান করতে সাহায্য করে। এই বিভাগে কীভাবে কম্পোজ করার উপযুক্ত আইটেমের সাধারণ প্রপার্টি অ্যানিমেট করতে হয় তা দেখানো হয়েছে।
দৃশ্যমান / অদৃশ্যমান হওয়ার অ্যানিমেশন
কোনও কম্পোজ করার উপযুক্ত আইটেম লুকাতে বা দেখাতে AnimatedVisibility ব্যবহার করুন। AnimatedVisibility-এর মধ্যে
Modifier.animateEnterExit() বাচ্চারা নিজেদের এন্ট্রি
বা এক্সিট ট্রানজিশনের জন্য ব্যবহার করতে পারবে।
var visible by remember { mutableStateOf(true) } // Animated visibility will eventually remove the item from the composition once the animation has finished. AnimatedVisibility(visible) { // your composable here // ... }
AnimatedVisibility-এর এন্ট্রি ও এক্সিট প্যারামিটার আপনাকে কনফিগার করতে দেয় যে
কোনও কম্পোজ করার উপযুক্ত আইটেম দেখা গেলে ও সরে গেলে সেটি কেমন আচরণ করবে। আরও তথ্যের জন্য সম্পূর্ণ
ডকুমেন্টেশন পড়ুন।
কম্পোজ করার উপযুক্ত আইটেমের ভিজিবিলিটি অ্যানিমেট করার আরেকটি বিকল্প হল, animateFloatAsState ব্যবহার করে সময়ের সাথে সাথে
আলফা অ্যানিমেট করা:
var visible by remember { mutableStateOf(true) } val animatedAlpha by animateFloatAsState( targetValue = if (visible) 1.0f else 0f, label = "alpha" ) Box( modifier = Modifier .size(200.dp) .graphicsLayer { alpha = animatedAlpha } .clip(RoundedCornerShape(8.dp)) .background(colorGreen) .align(Alignment.TopCenter) ) { }
তবে, আলফা পরিবর্তন করার সময় মনে রাখতে হবে যে কম্পোজ করার উপযুক্ত কম্পোজিশনে
থেকে যায় এবং যেখানে সেটি লেআউট করা হয়েছে সেই জায়গাটি দখল করে থাকে। এর ফলে
স্ক্রিন রিডার ও অন্যান্য অ্যাক্সেসিবিলিটি মেকানিজম এখনও স্ক্রিনে থাকা আইটেমটি
বিবেচনা করতে পারে। অন্যদিকে, AnimatedVisibility কম্পোজিশন থেকে
আইটেমটি সরিয়ে দেয়।
ব্যাকগ্রাউন্ডের রঙ অ্যানিমেট করা
val animatedColor by animateColorAsState( if (animateBackgroundColor) colorGreen else colorBlue, label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(animatedColor) } ) { // your composable here }
Modifier.background() ব্যবহার করার চেয়ে এই বিকল্পটি আরও ভাল পারফর্ম করে।
Modifier.background() একটি ওয়ান-শট কালার সেটিংয়ের জন্য গ্রহণযোগ্য, কিন্তু সময়ের সাথে সাথে
কোনও রঙ অ্যানিমেট করার সময়, এটি প্রয়োজনের চেয়ে বেশি
রিকম্পোজিশন করতে পারে।
ব্যাকগ্রাউন্ডের রঙকে অসীমভাবে অ্যানিমেট করতে, অ্যানিমেশন রিপিট করা বিভাগ দেখুন।
কম্পোজ করার উপযুক্ত আইটেমের সাইজ অ্যানিমেট করা
Compose আপনাকে কয়েকটি ভিন্ন উপায়ে কম্পোজ করার উপযুক্ত আইটেমের সাইজ অ্যানিমেট করতে দেয়। কম্পোজ করা যায় এমন সাইজের পরিবর্তনের মধ্যে অ্যানিমেশনের জন্য
animateContentSize() ব্যবহার করুন।
যেমন, আপনার কাছে এমন একটি বক্স আছে যাতে টেক্সট আছে যা একটি থেকে
একাধিক লাইনে প্রসারিত হতে পারে, আপনি আরও মসৃণ
ট্রানজিশন পেতে Modifier.animateContentSize() ব্যবহার করতে পারেন:
var expanded by remember { mutableStateOf(false) } Box( modifier = Modifier .background(colorBlue) .animateContentSize() .height(if (expanded) 400.dp else 200.dp) .fillMaxWidth() .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { expanded = !expanded } ) { }
এছাড়াও, সাইজ কীভাবে পরিবর্তন করা উচিত তা বর্ণনা করতে, আপনি AnimatedContent, সহ SizeTransform ব্যবহার করতে পারেন।
কম্পোজেবলের পজিশন অ্যানিমেট করা
কোনও কম্পোজ করার মতো আইটেমের পজিশন অ্যানিমেট করতে, Modifier.offset{ }-এর সাথে
animateIntOffsetAsState() ব্যবহার করুন।
var moved by remember { mutableStateOf(false) } val pxToMove = with(LocalDensity.current) { 100.dp.toPx().roundToInt() } val offset by animateIntOffsetAsState( targetValue = if (moved) { IntOffset(pxToMove, pxToMove) } else { IntOffset.Zero }, label = "offset" ) Box( modifier = Modifier .offset { offset } .background(colorBlue) .size(100.dp) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { moved = !moved } )
আপনি যদি নিশ্চিত করতে চান যে পজিশন বা সাইজ অ্যানিমেট করার সময় কম্পোজেবলগুলি অন্য কম্পোজেবলের
উপরে বা নিচে ড্র করা হচ্ছে না, তাহলে Modifier.layout{ } ব্যবহার করুন। এই
মডিফায়ারটি সাইজ ও পজিশন সংক্রান্ত পরিবর্তনকে পেরেন্ট এলিমেন্টে প্রোপাগেট করে, যা
অন্যান্য চাইল্ড এলিমেন্টকে প্রভাবিত করে।
যেমন, আপনি যদি Column-এর মধ্যে Box সরাচ্ছেন এবং 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) ) }
Modifier.layout{ }কম্পোজ করার উপযুক্ত আইটেমের প্যাডিং অ্যানিমেট করা
কোনও কম্পোজ করার মতো আইটেমের প্যাডিং অ্যানিমেট করতে, animateDpAsState-এর সাথে
Modifier.padding() ব্যবহার করুন:
var toggled by remember { mutableStateOf(false) } val animatedPadding by animateDpAsState( if (toggled) { 0.dp } else { 20.dp }, label = "padding" ) Box( modifier = Modifier .aspectRatio(1f) .fillMaxSize() .padding(animatedPadding) .background(Color(0xff53D9A1)) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { toggled = !toggled } )
কম্পোজেবলের এলিভেশন অ্যানিমেট করা
কোনও কম্পোজ করার মতো আইটেমের এলিভেশন অ্যানিমেট করতে, animateDpAsState-এর সাথে
Modifier.graphicsLayer{ } ব্যবহার করুন। একবারই উচ্চতা পরিবর্তন করার জন্য, Modifier.shadow() ব্যবহার করুন। আপনি যদি ছায়া অ্যানিমেট করেন, তাহলে
Modifier.graphicsLayer{ } মডিফায়ার ব্যবহার করা আরও পারফর্ম্যান্ট বিকল্প।
val mutableInteractionSource = remember { MutableInteractionSource() } val pressed = mutableInteractionSource.collectIsPressedAsState() val elevation = animateDpAsState( targetValue = if (pressed.value) { 32.dp } else { 8.dp }, label = "elevation" ) Box( modifier = Modifier .size(100.dp) .align(Alignment.Center) .graphicsLayer { this.shadowElevation = elevation.value.toPx() } .clickable(interactionSource = mutableInteractionSource, indication = null) { } .background(colorGreen) ) { }
বিকল্প হিসেবে, Card কম্পোজ করার উপযুক্ত ব্যবহার করুন এবং প্রতিটি স্টেট অনুযায়ী
আলাদা আলাদা ভ্যালুতে এলিভেশন প্রপার্টি সেট করুন।
টেক্সট স্কেল, ট্রান্সলেশন বা রোটেশন অ্যানিমেট করা
টেক্সটের স্কেল, ট্রান্সলেশন বা রোটেশন অ্যানিমেট করার সময়, TextStyle-এ textMotion
প্যারামিটারকে 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) ) }
টেক্সটের রঙ অ্যানিমেট করা
টেক্সটের রঙ অ্যানিমেট করতে, BasicText কম্পোজ করার উপযুক্ত আইটেমে color ল্যাম্বডা ব্যবহার করুন:
val infiniteTransition = rememberInfiniteTransition(label = "infinite transition") val animatedColor by infiniteTransition.animateColor( initialValue = Color(0xFF60DDAD), targetValue = Color(0xFF4285F4), animationSpec = infiniteRepeatable(tween(1000), RepeatMode.Reverse), label = "color" ) BasicText( text = "Hello Compose", color = { animatedColor }, // ... )
বিভিন্ন ধরনের কন্টেন্টের মধ্যে পরিবর্তন করা
আপনি যদি কম্পোজ করার উপযুক্ত আইটেমগুলির মধ্যে অ্যানিমেট করতে চান, তাহলে AnimatedContent ব্যবহার করুন।
আপনি যদি কম্পোজ করার উপযুক্ত আইটেমগুলির মধ্যে শুধু স্ট্যান্ডার্ড ফেড চান, তাহলে Crossfade ব্যবহার করুন।
var state by remember { mutableStateOf(UiState.Loading) } AnimatedContent( state, transitionSpec = { fadeIn( animationSpec = tween(3000) ) togetherWith fadeOut(animationSpec = tween(3000)) }, modifier = Modifier.clickable( interactionSource = remember { MutableInteractionSource() }, indication = null ) { state = when (state) { UiState.Loading -> UiState.Loaded UiState.Loaded -> UiState.Error UiState.Error -> UiState.Loading } }, label = "Animated Content" ) { targetState -> when (targetState) { UiState.Loading -> { LoadingScreen() } UiState.Loaded -> { LoadedScreen() } UiState.Error -> { ErrorScreen() } } }
AnimatedContent কাস্টমাইজ করে বিভিন্ন ধরনের এন্ট্রি ও
এক্সিট ট্রানজিশন দেখানো যায়। আরও তথ্যের জন্য, AnimatedContent লিঙ্কে ডকুমেন্টেশন পড়ুন অথবা
AnimatedContent লিঙ্কে এই ব্লগ পোস্ট পড়ুন।
বিভিন্ন গন্তব্যে নেভিগেট করার সময় অ্যানিমেট করা
navigation-compose আর্টিফ্যাক্ট ব্যবহার করার সময় কম্পোজ করার উপযুক্ত আইটেমের মধ্যে ট্রানজিশন অ্যানিমেট করতে, কম্পোজ করার উপযুক্ত আইটেমে enterTransition এবং
exitTransition নির্দিষ্ট করুন। এছাড়াও, আপনি টপ লেভেলে NavHost সব ডেস্টিনেশনের জন্য ব্যবহার করা হবে এমন ডিফল্ট অ্যানিমেশন সেট করতে পারেন:
val navController = rememberNavController() NavHost( navController = navController, startDestination = "landing", enterTransition = { EnterTransition.None }, exitTransition = { ExitTransition.None } ) { composable("landing") { ScreenLanding( // ... ) } composable( "detail/{photoUrl}", arguments = listOf(navArgument("photoUrl") { type = NavType.StringType }), enterTransition = { fadeIn( animationSpec = tween( 300, easing = LinearEasing ) ) + slideIntoContainer( animationSpec = tween(300, easing = EaseIn), towards = AnimatedContentTransitionScope.SlideDirection.Start ) }, exitTransition = { fadeOut( animationSpec = tween( 300, easing = LinearEasing ) ) + slideOutOfContainer( animationSpec = tween(300, easing = EaseOut), towards = AnimatedContentTransitionScope.SlideDirection.End ) } ) { backStackEntry -> ScreenDetails( // ... ) } }
এন্ট্রি ও এক্সিট ট্রানজিশনের অনেক আলাদা আলাদা ধরন আছে যা ইনকামিং ও আউটগোয়িং কন্টেন্টে আলাদা আলাদা এফেক্ট প্রয়োগ করে, আরও জানতে ডকুমেন্টেশন দেখুন।
অ্যানিমেশন রিপিট করা
আপনার অ্যানিমেশন একটানা রিপিট করতে rememberInfiniteTransition-এর সাথে infiniteRepeatable
animationSpec ব্যবহার করুন। এটি কীভাবে সামনে-পিছনে যাবে তা নির্দিষ্ট করতে RepeatModes পরিবর্তন করুন
।
নির্দিষ্ট সংখ্যক বার রিপিট করতে repeatable ব্যবহার করুন।
val infiniteTransition = rememberInfiniteTransition(label = "infinite") val color by infiniteTransition.animateColor( initialValue = Color.Green, targetValue = Color.Blue, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "color" ) Column( modifier = Modifier.drawBehind { drawRect(color) } ) { // your composable here }
কোনও কম্পোজ করার মতো আইটেম লঞ্চ করার সময় অ্যানিমেশন শুরু করা
কোনও কম্পোজ করার উপযুক্ত আইটেম কম্পোজিশনে প্রবেশ করলে LaunchedEffect রান করে। এটি কম্পোজ করার উপযুক্ত আইটেম লঞ্চ করার সময়
অ্যানিমেশন শুরু করে, আপনি অ্যানিমেশন
স্টেট পরিবর্তন করার জন্য এটি ব্যবহার করতে পারেন। লঞ্চের সময় অ্যানিমেশন শুরু করতে animateTo পদ্ধতির সাথে Animatable ব্যবহার করা:
val alphaAnimation = remember { Animatable(0f) } LaunchedEffect(Unit) { alphaAnimation.animateTo(1f) } Box( modifier = Modifier.graphicsLayer { alpha = alphaAnimation.value } )
ক্রমিক অ্যানিমেশন তৈরি করা
ক্রমিক বা কনকারেন্ট
অ্যানিমেশন পারফর্ম করতে Animatable করুটিন API ব্যবহার করুন। Animatable-এ animateTo কল করলে, একটি অ্যানিমেশন শুরু হওয়ার আগে
আগের অ্যানিমেশনটি শেষ হওয়ার জন্য অপেক্ষা করতে হয়।
এটি সাসপেন্ড ফাংশন হওয়ার কারণে এমন হয়।
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) অথবা
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-এ অ্যানিমেশন পারফর্ম্যান্স সংক্রান্ত সমস্যা সৃষ্টি করতে পারে। এর কারণ হল অ্যানিমেশনের প্রকৃতি: স্ক্রিনে দ্রুত পিক্সেল সরানো বা পরিবর্তন করা, ফ্রেম-বাই-ফ্রেম যাতে নড়াচড়া করার বিভ্রম তৈরি হয়।
কম্পোজ করার বিভিন্ন ধাপ বিবেচনা করুন: কম্পোজিশন, লেআউট ও ড্র। আপনার অ্যানিমেশন যদি লেআউট ফেজ পরিবর্তন করে, তাহলে প্রভাবিত হওয়া সব কম্পোজেবলকে আবার লেআউট ও ড্র করতে হবে। আপনার অ্যানিমেশন যদি ড্র ফেজে হয়, তাহলে সেটি লেআউট ফেজে অ্যানিমেশন রান করানোর চেয়ে ডিফল্ট হিসেবে বেশি পারফর্ম্যান্ট হবে, কারণ সামগ্রিকভাবে এটির কাজ কম করতে হবে।
অ্যানিমেট করার সময় আপনার অ্যাপ যাতে যতটা সম্ভব কম কাজ করে তা নিশ্চিত করতে, যেখানে সম্ভব সেখানে Modifier-এর ল্যাম্বডা
ভার্সন বেছে নিন। এটি রিকম্পোজিশন এড়িয়ে যায় এবং কম্পোজিশন ফেজের বাইরে
অ্যানিমেশন পারফর্ম করে, অন্যথায়
Modifier.graphicsLayer{ } ব্যবহার করুন, কারণ এই মডিফায়ার সবসময় ড্র
ফেজে রান করে। এই বিষয়ে আরও তথ্য পেতে, পারফর্ম্যান্স ডকুমেন্টেশনের
রিড ডিফার করা বিভাগ দেখুন।
অ্যানিমেশনের সময়সীমা পরিবর্তন করা
Compose ডিফল্ট হিসেবে বেশিরভাগ অ্যানিমেশনের জন্য স্প্রিং অ্যানিমেশন ব্যবহার করে। স্প্রিং বা
পদার্থবিদ্যা-ভিত্তিক অ্যানিমেশন আরও স্বাভাবিক মনে হয়। এছাড়াও, এগুলি বাধা দেওয়া যায় কারণ
এগুলি নির্দিষ্ট সময়ের পরিবর্তে অবজেক্টের বর্তমান বেগ বিবেচনা করে।
আপনি ডিফল্ট ওভাররাইড করতে চাইলে, আগে দেখানো সব অ্যানিমেশন API-তে
অ্যানিমেশন কীভাবে চলবে তা কাস্টমাইজ করার জন্য animationSpec সেট করার ক্ষমতা আছে।
আপনি কি চান যে এটি একটি নির্দিষ্ট সময় ধরে এক্সিকিউট হোক নাকি
আরও বেশি বাউন্সি হোক।
বিভিন্ন animationSpec বিকল্পের সারসংক্ষেপ নিচে দেওয়া হল:
spring: পদার্থবিদ্যা-ভিত্তিক অ্যানিমেশন, সব অ্যানিমেশনের জন্য ডিফল্ট। আপনি স্টিফনেস বা ড্যাম্পিং রেশিও পরিবর্তন করে অন্য ধরনের অ্যানিমেশন লুক অ্যান্ড ফিল পেতে পারেন।tween(বিটুইন-এর সংক্ষিপ্ত রূপ): সময়সীমা-ভিত্তিক অ্যানিমেশন,Easingফাংশনের সাহায্যে দুটি ভ্যালুর মধ্যে অ্যানিমেট করে।keyframes: কোনও অ্যানিমেশনের নির্দিষ্ট কী পয়েন্টে ভ্যালু নির্দিষ্ট করার স্পেক।repeatable: সময়সীমা-ভিত্তিক স্পেক যাRepeatMode-এর নির্দিষ্ট করা, নির্দিষ্ট সংখ্যক বার চলে।infiniteRepeatable: অনির্দিষ্টকালের জন্য চলতে থাকা সময়সীমা-ভিত্তিক স্পেক।snap: কোনও অ্যানিমেশন ছাড়াই শেষ ভ্যালুতে সঙ্গে সঙ্গে স্ন্যাপ করে।
animationSpecs সম্পর্কে আরও তথ্য পেতে সম্পূর্ণ ডকুমেন্টেশন পড়ুন।
অতিরিক্ত রিসোর্স
Compose-এ মজাদার অ্যানিমেশনের আরও উদাহরণ দেখতে, নিম্নলিখিত বিষয়গুলি দেখুন:
- Compose-এ ৫টি দ্রুত অ্যানিমেশন
- Compose-এ জেলিফিশকে নড়াচড়া করানো
- Compose-এ
AnimatedContentকাস্টমাইজ করা - Compose-এ ইজিং ফাংশন ব্যবহার করা