शेयर किए गए एलिमेंट के ट्रांज़िशन को पसंद के मुताबिक बनाएं

शेयर किए गए एलिमेंट के ट्रांज़िशन ऐनिमेशन को पसंद के मुताबिक बनाने के लिए, कुछ पैरामीटर का इस्तेमाल किया जा सकता है. इनसे, शेयर किए गए एलिमेंट के ट्रांज़िशन के तरीके में बदलाव किया जा सकता है.

ऐनिमेशन की खास जानकारी

साइज़ और पोज़िशन में बदलाव करने के लिए इस्तेमाल किए जाने वाले ऐनिमेशन की जानकारी बदलने के लिए, Modifier.sharedElement() पर कोई दूसरा boundsTransform पैरामीटर तय किया जा सकता है. इससे शुरुआती Rect पोज़िशन और टारगेट Rect पोज़िशन की जानकारी मिलती है.

उदाहरण के लिए, पिछले उदाहरण में दिए गए टेक्स्ट को आर्क मोशन के साथ मूव करने के लिए, boundsTransform पैरामीटर को keyframes स्पेक का इस्तेमाल करने के लिए सेट करें:

val textBoundsTransform = BoundsTransform { initialBounds, targetBounds ->
    keyframes {
        durationMillis = boundsAnimationDurationMillis
        initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing
        targetBounds at boundsAnimationDurationMillis
    }
}
Text(
    "Cupcake", fontSize = 28.sp,
    modifier = Modifier.sharedBounds(
        rememberSharedContentState(key = "title"),
        animatedVisibilityScope = animatedVisibilityScope,
        boundsTransform = textBoundsTransform
    )
)

आपके पास किसी भी AnimationSpec का इस्तेमाल करने का विकल्प है. इस उदाहरण में, keyframes स्पेसिफ़िकेशन का इस्तेमाल किया गया है.

पहली इमेज. अलग-अलग boundsTransform पैरामीटर
दिखाने वाला उदाहरण

साइज़ बदलने का मोड

शेयर किए गए दो बाउंड के बीच ऐनिमेशन बनाते समय, resizeMode पैरामीटर को SharedTransitionScope.ResizeMode.RemeasureToBounds या SharedTransitionScope.ResizeMode.scaleToBounds() पर सेट किया जा सकता है. यह पैरामीटर तय करता है कि शेयर किया गया एलिमेंट, दोनों स्थितियों के बीच कैसे ट्रांज़िशन करेगा. scaleToBounds() सबसे पहले, लुकअहेड (या टारगेट) कंस्ट्रेंट के साथ चाइल्ड लेआउट को मेज़र करता है. इसके बाद, चाइल्ड के स्टेबल लेआउट को शेयर किए गए बाउंड में फ़िट करने के लिए स्केल किया जाता है. scaleToBounds() को राज्यों के बीच "ग्राफ़िकल स्केल" के तौर पर देखा जा सकता है.

इसके उलट, RemeasureToBounds टारगेट साइज़ के आधार पर, ऐनिमेशन वाली तय सीमाओं के साथ sharedBounds के चाइल्ड लेआउट को फिर से मेज़र और लेआउट करता है. बाउंड के साइज़ में बदलाव होने पर, फिर से मेज़रमेंट ट्रिगर होता है. ऐसा हर फ़्रेम में हो सकता है.

Text कंपोज़ेबल के लिए, scaleToBounds() का सुझाव दिया जाता है. इससे टेक्स्ट को अलग-अलग लाइनों में फिर से लेआउट करने और फिर से फ़्लो करने से बचा जा सकता है. RemeasureToBounds का सुझाव उन बाउंड के लिए दिया जाता है जिनके आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) अलग-अलग होते हैं. साथ ही, अगर आपको शेयर किए गए दो एलिमेंट के बीच फ़्लूइड कंटिन्यूटी चाहिए, तो भी इसका सुझाव दिया जाता है.

साइज़ बदलने के दोनों मोड के बीच का अंतर, यहां दिए गए उदाहरणों में देखा जा सकता है:

scaleToBounds()

RemeasureToBounds

शेयर किए गए एलिमेंट को डाइनैमिक तौर पर चालू और बंद करना

डिफ़ॉल्ट रूप से, sharedElement() और sharedBounds() को इस तरह कॉन्फ़िगर किया जाता है कि टारगेट स्टेट में मैच होने वाली कुंजी मिलने पर, लेआउट में होने वाले बदलावों को ऐनिमेट किया जा सके. हालांकि, कुछ खास स्थितियों के आधार पर, इस ऐनिमेशन को डाइनैमिक तौर पर बंद किया जा सकता है. जैसे, नेविगेशन की दिशा या मौजूदा यूज़र इंटरफ़ेस (यूआई) की स्थिति.

शेयर किए गए एलिमेंट का ट्रांज़िशन होगा या नहीं, यह कंट्रोल करने के लिए, rememberSharedContentState() को पास किए गए SharedContentConfig को पसंद के मुताबिक बनाया जा सकता है. isEnabled प्रॉपर्टी से यह तय होता है कि शेयर किया गया एलिमेंट चालू है या नहीं.

यहां दिए गए उदाहरण में, एक ऐसा कॉन्फ़िगरेशन तय करने का तरीका बताया गया है जो सिर्फ़ कुछ स्क्रीन के बीच नेविगेट करते समय, शेयर किए गए ट्रांज़िशन को चालू करता है.जैसे, सिर्फ़ A से B पर जाने के लिए. साथ ही, अन्य स्क्रीन के लिए इसे बंद कर देता है.

SharedTransitionLayout {
    val transition = updateTransition(currentState)
    transition.AnimatedContent { targetState ->
        // Create the configuration that depends on state changing.
        fun animationConfig() : SharedTransitionScope.SharedContentConfig {
            return object : SharedTransitionScope.SharedContentConfig {
                override val SharedTransitionScope.SharedContentState.isEnabled: Boolean
                    // For this example, we only enable the transition in one direction
                    // from A -> B and not the other way around.
                    get() =
                        transition.currentState == "A" && transition.targetState == "B"
            }
        }
        when (targetState) {
            "A" -> Box(
                modifier = Modifier
                    .sharedElement(
                        rememberSharedContentState(
                            key = "shared_box",
                            config = animationConfig()
                        ),
                        animatedVisibilityScope = this
                    )
                    // ...
            ) {
                // Your content
            }
            "B" -> {
                Box(
                    modifier = Modifier
                        .sharedElement(
                            rememberSharedContentState(
                                key = "shared_box",
                                config = animationConfig()
                            ),
                            animatedVisibilityScope = this
                        )
                        // ...
                ) {
                    // Your content
                }
            }
        }
    }
}

डिफ़ॉल्ट रूप से, अगर किसी ऐनिमेशन के दौरान शेयर किए गए एलिमेंट को बंद कर दिया जाता है, तो भी वह मौजूदा ऐनिमेशन को पूरा करता है. इससे, चल रहे ऐनिमेशन को गलती से हटाने से रोका जा सकता है. अगर आपको ऐनिमेशन के दौरान एलिमेंट को हटाना है, तो SharedContentConfig इंटरफ़ेस में shouldKeepEnabledForOngoingAnimation को बदलकर, false वैल्यू रिटर्न की जा सकती है.

सीधे फ़ाइनल लेआउट पर जाएं

डिफ़ॉल्ट रूप से, दो लेआउट के बीच ट्रांज़िशन करते समय, लेआउट का साइज़ अपनी शुरुआती और आखिरी स्थिति के बीच ऐनिमेट होता है. टेक्स्ट जैसे कॉन्टेंट को ऐनिमेट करते समय, यह व्यवहार अनचाहा हो सकता है.

यहां दिए गए उदाहरण में, "Lorem Ipsum" टेक्स्ट को स्क्रीन पर दो अलग-अलग तरीकों से दिखाया गया है. पहले उदाहरण में, कंटेनर का साइज़ बढ़ने पर टेक्स्ट रीफ़्लो होता है. दूसरे उदाहरण में, टेक्स्ट के बढ़ने पर वह रीफ़्लो नहीं होता. Modifier.skipToLookaheadSize() को जोड़ने से, कॉन्टेंट के बढ़ने पर रीफ़्लो को रोका जा सकता है.

कोई Modifier.skipToLookaheadSize() नहीं - "Lorem Ipsum" टेक्स्ट को फिर से फ़्लो होते हुए देखें

Modifier.skipToLookaheadSize() - ध्यान दें कि "Lorem Ipsum" टेक्स्ट, ऐनिमेशन की शुरुआत में अपनी फ़ाइनल स्टेट में रहता है

क्लिप और ओवरले

शेयर किए गए एलिमेंट को अलग-अलग कंपोज़ेबल के बीच शेयर करने के लिए, ट्रांज़िशन शुरू होने पर कंपोज़ेबल की रेंडरिंग को लेयर ओवरले में बदल दिया जाता है. ऐसा तब होता है, जब डेस्टिनेशन में मौजूद मैच के लिए ट्रांज़िशन शुरू होता है. इसका असर यह होगा कि यह पैरंट की सीमाओं और लेयर ट्रांसफ़ॉर्मेशन (उदाहरण के लिए, ऐल्फ़ा और स्केल) से बाहर निकल जाएगा.

यह शेयर नहीं किए गए अन्य यूज़र इंटरफ़ेस (यूआई) एलिमेंट के ऊपर रेंडर होगा. ट्रांज़िशन पूरा होने के बाद, एलिमेंट को ओवरले से हटाकर उसके DrawScope पर छोड़ दिया जाएगा.

शेयर किए गए किसी एलिमेंट को किसी आकार में क्लिप करने के लिए, स्टैंडर्ड Modifier.clip() फ़ंक्शन का इस्तेमाल करें. इसे sharedElement() के बाद रखें:

Image(
    painter = painterResource(id = R.drawable.cupcake),
    contentDescription = "Cupcake",
    modifier = Modifier
        .size(100.dp)
        .sharedElement(
            rememberSharedContentState(key = "image"),
            animatedVisibilityScope = this@AnimatedContent
        )
        .clip(RoundedCornerShape(16.dp)),
    contentScale = ContentScale.Crop
)

अगर आपको यह पक्का करना है कि शेयर किया गया कोई एलिमेंट, पैरंट कंटेनर के बाहर कभी रेंडर न हो, तो sharedElement() पर clipInOverlayDuringTransition सेट करें. डिफ़ॉल्ट रूप से, नेस्ट किए गए शेयर किए गए बाउंड के लिए, clipInOverlayDuringTransition पैरंट sharedBounds() से क्लिप पाथ का इस्तेमाल करता है.

शेयर किए गए एलिमेंट के ट्रांज़िशन के दौरान, बॉटम बार या फ़्लोट करने वाले कार्रवाई बटन जैसे कुछ यूज़र इंटरफ़ेस (यूआई) एलिमेंट को हमेशा सबसे ऊपर बनाए रखने के लिए, Modifier.renderInSharedTransitionScopeOverlay() का इस्तेमाल करें. डिफ़ॉल्ट रूप से, यह मॉडिफ़ायर शेयर किए गए ट्रांज़िशन के चालू रहने के दौरान, कॉन्टेंट को ओवरले में रखता है.

उदाहरण के लिए, Jetsnack में, BottomAppBar को शेयर किए गए एलिमेंट के ऊपर तब तक रखना होगा, जब तक स्क्रीन न दिखे. कॉम्पोज़ेबल में मॉडिफ़ायर जोड़ने से, वह एलिवेट रहता है.

Modifier.renderInSharedTransitionScopeOverlay() के बिना

Modifier.renderInSharedTransitionScopeOverlay() के साथ

ऐसा हो सकता है कि आपको शेयर न किए गए कंपोज़ेबल को ऐनिमेट करके हटाना हो. साथ ही, ट्रांज़िशन से पहले उसे अन्य कंपोज़ेबल के ऊपर रखना हो. ऐसे मामलों में, शेयर किए गए एलिमेंट के ट्रांज़िशन के दौरान, कंपोज़ेबल को ऐनिमेट करने के लिए renderInSharedTransitionScopeOverlay().animateEnterExit() का इस्तेमाल करें:

JetsnackBottomBar(
    modifier = Modifier
        .renderInSharedTransitionScopeOverlay(
            zIndexInOverlay = 1f,
        )
        .animateEnterExit(
            enter = fadeIn() + slideInVertically {
                it
            },
            exit = fadeOut() + slideOutVertically {
                it
            }
        )
)

दूसरी इमेज. ऐनिमेशन ट्रांज़िशन के दौरान, बॉटम ऐप्लिकेशन बार का स्लाइड इन और स्लाइड आउट होना.

अगर आपको शेयर किए गए एलिमेंट को ओवरले में रेंडर नहीं करना है, तो sharedElement() पर renderInOverlayDuringTransition को false पर सेट करें. हालांकि, ऐसा बहुत कम मामलों में होता है.

शेयर किए गए एलिमेंट के साइज़ में हुए बदलावों के बारे में, सिबलिंग लेआउट को सूचना दें

डिफ़ॉल्ट रूप से, लेआउट ट्रांज़िशन के दौरान साइज़ में होने वाले किसी भी बदलाव की सूचना, sharedBounds() और sharedElement() पैरंट कंटेनर को नहीं देते.

ट्रांज़िशन के दौरान, पैरंट कंटेनर में साइज़ में हुए बदलावों को लागू करने के लिए, placeholderSize पैरामीटर को PlaceholderSize.AnimatedSize में बदलें. ऐसा करने से, आइटम बड़ा या छोटा हो जाता है. लेआउट में मौजूद अन्य सभी आइटम, इस बदलाव के हिसाब से अडजस्ट हो जाते हैं.

PlaceholderSize.ContentSize (डिफ़ॉल्ट)

PlaceholderSize.AnimatedSize

(ध्यान दें कि सूची में मौजूद एक आइटम का साइज़ बढ़ने पर, अन्य आइटम कैसे नीचे की ओर खिसकते हैं)