इस पेज पर, Jetpack Compose में वैल्यू के आधार पर ऐनिमेशन बनाने का तरीका बताया गया है. इसमें उन एपीआई पर फ़ोकस किया गया है जो वैल्यू की मौजूदा और टारगेट की गई स्थितियों के आधार पर ऐनिमेशन बनाते हैं.
animate*AsState
की मदद से किसी वैल्यू को ऐनिमेट करना
animate*AsState
फ़ंक्शन, Compose में एक वैल्यू को ऐनिमेट करने के लिए, आसान एनिमेशन एपीआई हैं. आपको सिर्फ़ टारगेट वैल्यू (या आखिरी वैल्यू) देनी होती है. इसके बाद, एपीआई मौजूदा वैल्यू से लेकर तय की गई वैल्यू तक ऐनिमेशन शुरू करता है.
इस उदाहरण में, इस एपीआई का इस्तेमाल करके ऐल्फ़ा को ऐनिमेट किया गया है. टारगेट वैल्यू को animateFloatAsState
में रैप करने पर, ऐल्फ़ा वैल्यू अब दी गई वैल्यू (इस मामले में 1f
या 0.5f
) के बीच की ऐनिमेशन वैल्यू बन जाती है.
var enabled by remember { mutableStateOf(true) } val animatedAlpha: Float by animateFloatAsState(if (enabled) 1f else 0.5f, label = "alpha") Box( Modifier .fillMaxSize() .graphicsLayer { alpha = animatedAlpha } .background(Color.Red) )
आपको किसी भी ऐनिमेशन क्लास का इंस्टेंस बनाने या रुकावट को हैंडल करने की ज़रूरत नहीं है. बैकग्राउंड में, एक ऐनिमेशन ऑब्जेक्ट (यानी कि एक Animatable
instance) बनाया जाएगा और कॉल साइट पर सेव किया जाएगा. इसकी शुरुआती वैल्यू, पहले टारगेट की वैल्यू होगी. इसके बाद, जब भी इस कंपोज़ेबल को कोई दूसरी टारगेट वैल्यू दी जाती है, तो उस वैल्यू के लिए ऐनिमेशन अपने-आप शुरू हो जाता है. अगर पहले से कोई ऐनिमेशन चल रहा है, तो ऐनिमेशन अपनी मौजूदा वैल्यू (और वेलोसिटी) से शुरू होता है और टारगेट वैल्यू की ओर ऐनिमेट होता है. ऐनिमेशन के दौरान, यह कंपोज़ेबल फिर से कंपोज़ होता है. साथ ही, हर फ़्रेम में ऐनिमेशन की अपडेट की गई वैल्यू दिखाता है.
डिफ़ॉल्ट रूप से, Compose Float
, Color
, Dp
, Size
, Offset
, Rect
, Int
, IntOffset
, और IntSize
के लिए animate*AsState
फ़ंक्शन उपलब्ध कराता है. सामान्य टाइप लेने वाले animateValueAsState
को TwoWayConverter
देकर, अन्य डेटा टाइप के लिए सहायता जोड़ी जा सकती है.
AnimationSpec
देकर, ऐनिमेशन के स्पेसिफ़िकेशन को अपनी पसंद के मुताबिक बनाया जा सकता है. ज़्यादा जानकारी के लिए, AnimationSpec
पर जाएं.
ट्रांज़िशन का इस्तेमाल करके, एक साथ कई प्रॉपर्टी में ऐनिमेशन जोड़ना
Transition
, एक या उससे ज़्यादा ऐनिमेशन को अपने चाइल्ड के तौर पर मैनेज करता है और उन्हें एक साथ कई स्थितियों के बीच चलाता है.
स्टेट किसी भी डेटा टाइप की हो सकती हैं. ज़्यादातर मामलों में, टाइप की सुरक्षा की पुष्टि करने के लिए कस्टम enum
टाइप का इस्तेमाल किया जा सकता है. जैसे, इस उदाहरण में:
enum class BoxState { Collapsed, Expanded }
updateTransition
, Transition
का एक इंस्टेंस बनाता है और उसे याद रखता है. साथ ही, उसकी स्थिति को अपडेट करता है.
var currentState by remember { mutableStateOf(BoxState.Collapsed) } val transition = updateTransition(currentState, label = "box state")
इसके बाद, इस ट्रांज़िशन में चाइल्ड ऐनिमेशन तय करने के लिए, animate*
एक्सटेंशन फ़ंक्शन में से किसी एक का इस्तेमाल किया जा सकता है. हर राज्य के लिए टारगेट वैल्यू तय करें.
ये animate*
फ़ंक्शन, ऐनिमेशन की वैल्यू दिखाते हैं. ऐनिमेशन के दौरान, हर फ़्रेम पर यह वैल्यू अपडेट होती है. ऐसा तब होता है, जब ट्रांज़िशन की स्थिति को updateTransition
से अपडेट किया जाता है.
val rect by transition.animateRect(label = "rectangle") { state -> when (state) { BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f) BoxState.Expanded -> Rect(100f, 100f, 300f, 300f) } } val borderWidth by transition.animateDp(label = "border width") { state -> when (state) { BoxState.Collapsed -> 1.dp BoxState.Expanded -> 0.dp } }
इसके अलावा, transitionSpec
पैरामीटर पास करके, ट्रांज़िशन की स्थिति में होने वाले बदलावों के हर कॉम्बिनेशन के लिए अलग-अलग AnimationSpec
तय किया जा सकता है. ज़्यादा जानकारी के लिए, AnimationSpec
देखें.
val color by transition.animateColor( transitionSpec = { when { BoxState.Expanded isTransitioningTo BoxState.Collapsed -> spring(stiffness = 50f) else -> tween(durationMillis = 500) } }, label = "color" ) { state -> when (state) { BoxState.Collapsed -> MaterialTheme.colorScheme.primary BoxState.Expanded -> MaterialTheme.colorScheme.background } }
जब ट्रांज़िशन टारगेट स्टेट पर पहुंच जाता है, तब Transition.currentState
, Transition.targetState
के जैसा ही होता है. इसका इस्तेमाल यह पता लगाने के लिए किया जा सकता है कि ट्रांज़िशन पूरा हो गया है या नहीं.
कभी-कभी, आपको शुरुआती स्थिति को पहले टारगेट की स्थिति से अलग रखना पड़ सकता है. इसके लिए, MutableTransitionState
के साथ updateTransition
का इस्तेमाल किया जा सकता है. उदाहरण के लिए, इससे कोड के कंपोज़िशन में शामिल होते ही ऐनिमेशन शुरू किया जा सकता है.
// Start in collapsed state and immediately animate to expanded var currentState = remember { MutableTransitionState(BoxState.Collapsed) } currentState.targetState = BoxState.Expanded val transition = rememberTransition(currentState, label = "box state") // ……
एक से ज़्यादा कंपोज़ेबल फ़ंक्शन वाले ज़्यादा जटिल ट्रांज़िशन के लिए, चाइल्ड ट्रांज़िशन बनाने के लिए createChildTransition
का इस्तेमाल किया जा सकता है. यह तकनीक, कंपोज़ेबल कॉम्पोनेंट में मौजूद कई सबकॉम्पोनेंट के बीच अलग-अलग काम बांटने के लिए फ़ायदेमंद होती है. पैरंट ट्रांज़िशन को चाइल्ड ट्रांज़िशन में मौजूद सभी ऐनिमेशन वैल्यू के बारे में पता होता है.
enum class DialerState { DialerMinimized, NumberPad } @Composable fun DialerButton(isVisibleTransition: Transition<Boolean>) { // `isVisibleTransition` spares the need for the content to know // about other DialerStates. Instead, the content can focus on // animating the state change between visible and not visible. } @Composable fun NumberPad(isVisibleTransition: Transition<Boolean>) { // `isVisibleTransition` spares the need for the content to know // about other DialerStates. Instead, the content can focus on // animating the state change between visible and not visible. } @Composable fun Dialer(dialerState: DialerState) { val transition = updateTransition(dialerState, label = "dialer state") Box { // Creates separate child transitions of Boolean type for NumberPad // and DialerButton for any content animation between visible and // not visible NumberPad( transition.createChildTransition { it == DialerState.NumberPad } ) DialerButton( transition.createChildTransition { it == DialerState.DialerMinimized } ) } }
AnimatedVisibility
और AnimatedContent
के साथ ट्रांज़िशन का इस्तेमाल करना
AnimatedVisibility
और AnimatedContent
, Transition
के एक्सटेंशन फ़ंक्शन के तौर पर उपलब्ध हैं. Transition.AnimatedVisibility
और Transition.AnimatedContent
के लिए targetState
, Transition
से लिया जाता है. साथ ही, Transition
के targetState
में बदलाव होने पर, ज़रूरत के हिसाब से एंट्री, एग्ज़िट, और sizeTransform
ऐनिमेशन ट्रिगर करता है. इन एक्सटेंशन फ़ंक्शन की मदद से, sizeTransform
/AnimatedContent
के अंदर मौजूद सभी एंटर, एक्ज़िट, और sizeTransform
ऐनिमेशन को Transition
में ले जाया जा सकता है.AnimatedVisibility
इन एक्सटेंशन फ़ंक्शन की मदद से, AnimatedVisibility
/AnimatedContent
की स्थिति में होने वाले बदलाव को बाहर से देखा जा सकता है. बूलियन visible
पैरामीटर के बजाय, AnimatedVisibility
के इस वर्शन में एक लैम्डा होता है. यह लैम्डा, पैरंट ट्रांज़िशन की टारगेट स्थिति को बूलियन में बदलता है.
ज़्यादा जानकारी के लिए, AnimatedVisibility
और AnimatedContent
देखें.
var selected by remember { mutableStateOf(false) } // Animates changes when `selected` is changed. val transition = updateTransition(selected, label = "selected state") val borderColor by transition.animateColor(label = "border color") { isSelected -> if (isSelected) Color.Magenta else Color.White } val elevation by transition.animateDp(label = "elevation") { isSelected -> if (isSelected) 10.dp else 2.dp } Surface( onClick = { selected = !selected }, shape = RoundedCornerShape(8.dp), border = BorderStroke(2.dp, borderColor), shadowElevation = elevation ) { Column( modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { Text(text = "Hello, world!") // AnimatedVisibility as a part of the transition. transition.AnimatedVisibility( visible = { targetSelected -> targetSelected }, enter = expandVertically(), exit = shrinkVertically() ) { Text(text = "It is fine today.") } // AnimatedContent as a part of the transition. transition.AnimatedContent { targetState -> if (targetState) { Text(text = "Selected") } else { Icon(imageVector = Icons.Default.Phone, contentDescription = "Phone") } } } }
ट्रांज़िशन को कैप्सूल में रखना और उसे फिर से इस्तेमाल करने लायक बनाना
आसान इस्तेमाल के मामलों के लिए, ट्रांज़िशन ऐनिमेशन को यूज़र इंटरफ़ेस (यूआई) के तौर पर एक ही कंपोज़ेबल में तय करना एक मान्य विकल्प है. हालांकि, जब आपको किसी ऐसे मुश्किल कॉम्पोनेंट पर काम करना हो जिसमें कई ऐनिमेशन वाली वैल्यू हों, तो आपको ऐनिमेशन लागू करने वाले कोड को कंपोज़ेबल यूज़र इंटरफ़ेस (यूआई) से अलग करना पड़ सकता है.
इसके लिए, एक ऐसी क्लास बनाएं जिसमें ऐनिमेशन की सभी वैल्यू मौजूद हों. साथ ही, एक ऐसा update
फ़ंक्शन बनाएं जो उस क्लास का इंस्टेंस दिखाता हो. ट्रांज़िशन लागू करने के तरीके को एक नए फ़ंक्शन में अलग किया जा सकता है. यह पैटर्न तब काम आता है, जब आपको ऐनिमेशन लॉजिक को एक जगह पर रखना हो या जटिल ऐनिमेशन को फिर से इस्तेमाल करने लायक बनाना हो.
enum class BoxState { Collapsed, Expanded } @Composable fun AnimatingBox(boxState: BoxState) { val transitionData = updateTransitionData(boxState) // UI tree Box( modifier = Modifier .background(transitionData.color) .size(transitionData.size) ) } // Holds the animation values. private class TransitionData( color: State<Color>, size: State<Dp> ) { val color by color val size by size } // Create a Transition and return its animation values. @Composable private fun updateTransitionData(boxState: BoxState): TransitionData { val transition = updateTransition(boxState, label = "box state") val color = transition.animateColor(label = "color") { state -> when (state) { BoxState.Collapsed -> Color.Gray BoxState.Expanded -> Color.Red } } val size = transition.animateDp(label = "size") { state -> when (state) { BoxState.Collapsed -> 64.dp BoxState.Expanded -> 128.dp } } return remember(transition) { TransitionData(color, size) } }
rememberInfiniteTransition
की मदद से, बार-बार चलने वाला ऐनिमेशन बनाना
InfiniteTransition
में एक या उससे ज़्यादा चाइल्ड ऐनिमेशन होते हैं, जैसे कि Transition
,
लेकिन ये ऐनिमेशन कंपोज़िशन में शामिल होते ही चलने लगते हैं और इन्हें तब तक नहीं रोका जा सकता, जब तक इन्हें हटाया न जाए. rememberInfiniteTransition
की मदद से, InfiniteTransition
का इंस्टेंस बनाया जा सकता है. साथ ही, animateColor
, animatedFloat
या animatedValue
की मदद से, चाइल्ड ऐनिमेशन जोड़े जा सकते हैं. ऐनिमेशन की खास जानकारी देने के लिए, आपको infiniteRepeatable
भी तय करना होगा.
val infiniteTransition = rememberInfiniteTransition(label = "infinite") val color by infiniteTransition.animateColor( initialValue = Color.Red, targetValue = Color.Green, animationSpec = infiniteRepeatable( animation = tween(1000, easing = LinearEasing), repeatMode = RepeatMode.Reverse ), label = "color" ) Box( Modifier .fillMaxSize() .background(color) )
लो-लेवल ऐनिमेशन एपीआई
ऊपर दिए गए सेक्शन में बताए गए सभी हाई-लेवल ऐनिमेशन एपीआई, लो-लेवल ऐनिमेशन एपीआई पर आधारित होते हैं.
animate*AsState
फ़ंक्शन, सीधे तौर पर इस्तेमाल किए जा सकने वाले एपीआई हैं. ये किसी वैल्यू में होने वाले बदलाव को तुरंत ऐनिमेशन वैल्यू के तौर पर रेंडर करते हैं. यह सुविधा, Animatable
की मदद से काम करती है. यह एक को-रूटीन पर आधारित एपीआई है, जिसका इस्तेमाल किसी वैल्यू को ऐनिमेट करने के लिए किया जाता है.
updateTransition
एक ट्रांज़िशन ऑब्जेक्ट बनाता है. यह ऑब्जेक्ट, ऐनिमेशन वाली कई वैल्यू को मैनेज कर सकता है. साथ ही, स्थिति बदलने पर उन्हें चला सकता है. rememberInfiniteTransition
भी इसी तरह का होता है. हालांकि, यह एक ऐसा ट्रांज़िशन बनाता है जो कभी खत्म नहीं होता. इससे कई ऐसे ऐनिमेशन मैनेज किए जा सकते हैं जो हमेशा चलते रहते हैं. Animatable
को छोड़कर, ये सभी एपीआई कंपोज़ेबल हैं. इसका मतलब है कि कंपोज़िशन के बाहर भी इन ऐनिमेशन को बनाया जा सकता है.
ये सभी एपीआई, ज़्यादा बुनियादी Animation
एपीआई पर आधारित हैं. ज़्यादातर ऐप्लिकेशन, Animation
के साथ सीधे तौर पर इंटरैक्ट नहीं करते. हालांकि, हायर-लेवल एपीआई के ज़रिए, Animation
की कुछ कस्टम बनाने की सुविधाओं को ऐक्सेस किया जा सकता है. AnimationVector
और AnimationSpec
के बारे में ज़्यादा जानने के लिए, ऐनिमेशन को पसंद के मुताबिक बनाएं लेख पढ़ें.
Animatable
: कोरूटीन पर आधारित सिंगल वैल्यू ऐनिमेशन
Animatable
एक वैल्यू होल्डर है. यह animateTo
का इस्तेमाल करके, वैल्यू में बदलाव होने पर उसे ऐनिमेट कर सकता है. यह एपीआई, animate*AsState
को लागू करने में मदद करता है. यह लगातार बदलाव और एक-दूसरे से अलग होने की सुविधा देता है. इसका मतलब है कि वैल्यू में बदलाव हमेशा लगातार होता है और Compose, चल रहे किसी भी एनिमेशन को रद्द कर देता है.
Animatable
की कई सुविधाएं, जैसे कि animateTo
, सस्पेंड फ़ंक्शन हैं.
इसका मतलब है कि आपको उन्हें सही को-रूटीन स्कोप में रैप करना होगा. उदाहरण के लिए,
LaunchedEffect
कंपोज़ेबल का इस्तेमाल करके, सिर्फ़ तय की गई कुंजी की वैल्यू की अवधि के लिए स्कोप बनाया जा सकता है.
// Start out gray and animate to green/red based on `ok` val color = remember { Animatable(Color.Gray) } LaunchedEffect(ok) { color.animateTo(if (ok) Color.Green else Color.Red) } Box( Modifier .fillMaxSize() .background(color.value) )
ऊपर दिए गए उदाहरण में, Color.Gray
की शुरुआती वैल्यू के साथ Animatable
का एक इंस्टेंस बनाया जाता है और उसे सेव किया जाता है. बूलियन फ़्लैग ok
की वैल्यू के आधार पर, रंग Color.Green
या Color.Red
में बदलता है. बूलियन वैल्यू में बाद में होने वाले किसी भी बदलाव से, दूसरे रंग में ऐनिमेशन शुरू हो जाता है.
अगर वैल्यू बदलते समय कोई एनिमेशन चल रहा है, तो Compose उस एनिमेशन को रद्द कर देता है. इसके बाद, नया एनिमेशन, मौजूदा स्नैपशॉट वैल्यू से शुरू होता है. साथ ही, इसमें मौजूदा वेलोसिटी का इस्तेमाल किया जाता है.
यह Animatable
एपीआई, पिछले सेक्शन में बताए गए animate*AsState
को लागू करने के लिए इस्तेमाल किया जाता है. Animatable
का सीधे तौर पर इस्तेमाल करने से, कई तरीकों से बेहतर कंट्रोल मिलता है:
- पहला,
Animatable
की शुरुआती वैल्यू, पहले टारगेट वैल्यू से अलग हो सकती है. उदाहरण के लिए, ऊपर दिए गए कोड के उदाहरण में, पहले एक ग्रे बॉक्स दिखता है. इसके बाद, वह तुरंत हरे या लाल रंग में बदल जाता है. - दूसरा,
Animatable
कॉन्टेंट वैल्यू पर ज़्यादा कार्रवाइयां करने की सुविधा देता है. खास तौर पर,snapTo
औरanimateDecay
.snapTo
, मौजूदा वैल्यू को तुरंत टारगेट वैल्यू पर सेट करता है. यह तब काम आता है, जब ऐनिमेशन ही सच्चाई का एकमात्र सोर्स न हो और उसे टच इवेंट जैसी अन्य स्थितियों के साथ सिंक करना हो.animateDecay
एक ऐसा ऐनिमेशन शुरू करता है जो दी गई वेलोसिटी से धीरे-धीरे कम होता जाता है. यह फ़्लिंग के व्यवहार को लागू करने के लिए काम आता है.
ज़्यादा जानकारी के लिए, हाव-भाव और ऐनिमेशन देखें.
डिफ़ॉल्ट रूप से, Animatable
Float
और Color
के साथ काम करता है. हालांकि, TwoWayConverter
देकर किसी भी डेटा टाइप का इस्तेमाल किया जा सकता है. ज़्यादा जानकारी के लिए, AnimationVector देखें.
AnimationSpec
देकर, ऐनिमेशन के स्पेसिफ़िकेशन को अपनी पसंद के मुताबिक बनाया जा सकता है.
ज़्यादा जानकारी के लिए, AnimationSpec
पर जाएं.
Animation
: मैन्युअल तरीके से कंट्रोल किया जाने वाला ऐनिमेशन
Animation
, सबसे कम लेवल वाला Animation API है. अब तक हमने जितने भी ऐनिमेशन देखे हैं वे Animation
पर आधारित हैं. इसके दो Animation
सबटाइप होते हैं: TargetBasedAnimation
और DecayAnimation
.
ऐनिमेशन के समय को मैन्युअल तरीके से कंट्रोल करने के लिए, सिर्फ़ Animation
का इस्तेमाल करें. Animation
स्टेटलेस है. साथ ही, इसमें लाइफ़साइकल का कोई कॉन्सेप्ट नहीं है. यह ज़्यादा लेवल वाले एपीआई के लिए, ऐनिमेशन कैलकुलेशन इंजन के तौर पर काम करता है.
TargetBasedAnimation
ज़्यादातर मामलों में, अन्य एपीआई का इस्तेमाल किया जा सकता है. हालांकि, TargetBasedAnimation
का सीधे तौर पर इस्तेमाल करने से, आपको ऐनिमेशन के चलने की अवधि को कंट्रोल करने की सुविधा मिलती है. यहां दिए गए उदाहरण में, withFrameNanos
से मिले फ़्रेम टाइम के आधार पर, TargetAnimation
के चलने की अवधि को मैन्युअल तरीके से कंट्रोल किया जाता है.
val anim = remember { TargetBasedAnimation( animationSpec = tween(200), typeConverter = Float.VectorConverter, initialValue = 200f, targetValue = 1000f ) } var playTime by remember { mutableLongStateOf(0L) } LaunchedEffect(anim) { val startTime = withFrameNanos { it } do { playTime = withFrameNanos { it } - startTime val animationValue = anim.getValueFromNanos(playTime) } while (someCustomCondition()) }
DecayAnimation
TargetBasedAnimation
के उलट, DecayAnimation
के लिए targetValue
की जानकारी देना ज़रूरी नहीं है. इसके बजाय, यह targetValue
के आधार पर अपना हिसाब लगाता है. initialVelocity
और initialValue
ने शुरुआती शर्तें सेट की हैं. साथ ही, DecayAnimationSpec
उपलब्ध कराया गया है.
फ़्लिंग जेस्चर के बाद, अक्सर डेके ऐनिमेशन का इस्तेमाल किया जाता है, ताकि एलिमेंट की स्पीड कम हो जाए और वे रुक जाएं. ऐनिमेशन की वेलोसिटी, initialVelocityVector
सेट की गई वैल्यू से शुरू होती है और समय के साथ कम होती जाती है.
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक टेक्स्ट दिखता है
- ऐनिमेशन को पसंद के मुताबिक बनाना
- Compose में ऐनिमेशन
- ऐनिमेशन मॉडिफ़ायर और कंपोज़ेबल