Compose-এ অনেক বিল্ট-ইন অ্যানিমেশন মেকানিজম আছে এবং কোনটি বেছে নিতে হবে তা জানা অত্যন্ত কঠিন হতে পারে। নিচে অ্যানিমেশনের সাধারণ ব্যবহারের তালিকা দেওয়া হল। আপনার জন্য উপলভ্য বিভিন্ন API বিকল্পের সম্পূর্ণ সেট সম্পর্কে আরও বিস্তারিত তথ্যের জন্য, সম্পূর্ণ Compose অ্যানিমেশন ডকুমেন্টেশন পড়ুন।
সাধারণ কম্পোজেবল প্রপার্টি অ্যানিমেট করুন
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 } ) { }
এছাড়াও, আপনি SizeTransform-এর সাথে AnimatedContent ব্যবহার করে
সাইজ কীভাবে পরিবর্তন করা উচিত তা বর্ণনা করতে পারেন।
কম্পোজেবলের পজিশন অ্যানিমেট করুন
কম্পোজেবলের পজিশন অ্যানিমেট করতে, 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( // ... ) } }
বিভিন্ন ধরনের এন্টার ও এক্সিট ট্রানজিশন আছে যা ইনকামিং ও আউটগোয়িং কন্টেন্টে বিভিন্ন এফেক্ট প্রয়োগ করে, আরও জানতে ডকুমেন্টেশন দেখুন।
অ্যানিমেশন রিপিট করা
আপনার অ্যানিমেশন ক্রমাগত রিপিট করতে infiniteRepeatable
animationSpec-এর সাথে rememberInfiniteTransition ব্যবহার করুন। 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 coroutine 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)) }
কনকারেন্ট অ্যানিমেশন তৈরি করুন
একসাথে অ্যানিমেশন চালানোর জন্য coroutine 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: পদার্থবিদ্যা-ভিত্তিক অ্যানিমেশন, সব অ্যানিমেশনের জন্য ডিফল্ট। আপনি অ্যানিমেশনের লুক ও ফিল পরিবর্তন করতে stiffness বা dampingRatio পরিবর্তন করতে পারেন।tween(between-এর সংক্ষিপ্ত রূপ): সময়কাল-ভিত্তিক অ্যানিমেশন,Easingফাংশন ব্যবহার করে দুটি মানের মধ্যে অ্যানিমেট করে।keyframes: অ্যানিমেশনের নির্দিষ্ট কিছু কী পয়েন্টে ভ্যালু নির্দিষ্ট করার জন্য স্পেসিফিকেশন।repeatable: সময়সীমা-ভিত্তিক স্পেসিফিকেশন যা নির্দিষ্ট সংখ্যক বার চলে,RepeatModeদ্বারা নির্দিষ্ট করা হয়।infiniteRepeatable: সময়সীমা-ভিত্তিক স্পেসিফিকেশন যা চিরকাল চলে।snap: কোনও অ্যানিমেশন ছাড়াই শেষ ভ্যালুতে ঝটপট স্ন্যাপ করে।
animationSpecs সম্পর্কে আরও তথ্যের জন্য সম্পূর্ণ ডকুমেন্টেশন পড়ুন।
অতিরিক্ত রিসোর্স
Compose-এ মজাদার অ্যানিমেশনের আরও উদাহরণের জন্য, নিম্নলিখিতগুলি দেখুন:
- Compose-এ ৫টি দ্রুত অ্যানিমেশন
- Compose-এ জেলিফিশকে নড়াচড়া করানো
- Compose-এ
AnimatedContentকাস্টমাইজ করা - Compose-এ ইজিং ফাংশনে ইজিং করা