ভাগ করা উপাদান স্থানান্তর কাস্টমাইজ করুন

শেয়ার্ড এলিমেন্ট ট্রানজিশন অ্যানিমেশন কীভাবে চলে তা কাস্টমাইজ করার জন্য, কিছু প্যারামিটার ব্যবহার করে শেয়ার্ড এলিমেন্ট ট্রানজিশন কীভাবে হয় তা পরিবর্তন করা যেতে পারে।

অ্যানিমেশন স্পেক

আকার এবং অবস্থানের গতিবিধির জন্য ব্যবহৃত অ্যানিমেশন স্পেক পরিবর্তন করতে, আপনি Modifier.sharedElement() এ একটি ভিন্ন boundsTransform প্যারামিটার নির্দিষ্ট করতে পারেন। এটি প্রাথমিক Rect অবস্থান এবং লক্ষ্য Rect অবস্থান প্রদান করে।

উদাহরণস্বরূপ, পূর্ববর্তী উদাহরণের লেখাটিকে একটি আর্ক মোশনের সাথে সরানোর জন্য, একটি keyframes স্পেক ব্যবহার করার জন্য boundsTransform প্যারামিটারটি নির্দিষ্ট করুন:

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 spec ব্যবহার করা হয়েছে।

চিত্র ১. বিভিন্ন boundsTransform দেখানোর উদাহরণ পরামিতি রূপান্তর করুন

আকার পরিবর্তন মোড

দুটি ভাগ করা সীমানার মধ্যে অ্যানিমেট করার সময়, আপনি resizeMode প্যারামিটারটি RemeasureToBounds অথবা ScaleToBounds এ সেট করতে পারেন। এই প্যারামিটারটি নির্ধারণ করে যে দুটি অবস্থার মধ্যে ভাগ করা উপাদান কীভাবে রূপান্তরিত হয়। ScaleToBounds প্রথমে লুকহেড (অথবা লক্ষ্য) সীমাবদ্ধতা দিয়ে চাইল্ড লেআউট পরিমাপ করে। তারপর, সন্তানের স্থিতিশীল লেআউটটি ভাগ করা সীমানার সাথে মানানসই করে স্কেল করা হয়। ScaleToBounds স্টেটগুলির মধ্যে একটি "গ্রাফিক্যাল স্কেল" হিসাবে ভাবা যেতে পারে।

বিপরীতে, RemeasureToBounds লক্ষ্য আকারের উপর ভিত্তি করে অ্যানিমেটেড স্থির সীমাবদ্ধতা সহ sharedBounds এর চাইল্ড লেআউটকে পুনরায় পরিমাপ এবং পুনরায় লেআউট করে। পুনঃপরিমাপটি সীমার আকার পরিবর্তনের মাধ্যমে ট্রিগার করা হয়, যা সম্ভাব্যভাবে প্রতিটি ফ্রেমের জন্য হতে পারে।

Text কম্পোজেবলের জন্য, ScaleToBounds সুপারিশ করা হয়, কারণ এটি বিভিন্ন লাইনে টেক্সটের রিলেআউট এবং রিফ্লোয়িং এড়ায়। RemeasureToBounds সুপারিশ করা হয় বিভিন্ন আকৃতির অনুপাতের সীমানার জন্য, এবং যদি আপনি দুটি ভাগ করা উপাদানের মধ্যে তরল ধারাবাহিকতা চান।

দুটি আকার পরিবর্তন মোডের মধ্যে পার্থক্য নিম্নলিখিত উদাহরণগুলিতে দেখা যাবে:

ScaleToBounds

RemeasureToBounds

ভাগ করা উপাদানগুলিকে গতিশীলভাবে সক্ষম এবং অক্ষম করুন

ডিফল্টরূপে, sharedElement() এবং sharedBounds() টার্গেট অবস্থায় কোনও মিলযুক্ত কী পাওয়া গেলে লেআউট পরিবর্তনগুলিকে অ্যানিমেট করার জন্য কনফিগার করা হয়। তবে, আপনি নেভিগেশনের দিকনির্দেশনা বা বর্তমান UI অবস্থার মতো নির্দিষ্ট অবস্থার উপর ভিত্তি করে এই অ্যানিমেশনটি গতিশীলভাবে অক্ষম করতে চাইতে পারেন।

শেয়ার্ড এলিমেন্ট ট্রানজিশন হবে কিনা তা নিয়ন্ত্রণ করতে, আপনি 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 ওভাররাইড করতে পারেন।

চূড়ান্ত লেআউটে যান

ডিফল্টরূপে, দুটি লেআউটের মধ্যে স্থানান্তরের সময়, লেআউটের আকার তার শুরু এবং চূড়ান্ত অবস্থার মধ্যে অ্যানিমেট করে। টেক্সটের মতো কন্টেন্ট অ্যানিমেট করার সময় এটি অবাঞ্ছিত আচরণ হতে পারে।

নিচের উদাহরণে "Lorem Ipsum" বর্ণনামূলক টেক্সটটি দুটি ভিন্ন উপায়ে স্ক্রিনে প্রবেশ করছে তা দেখানো হয়েছে। প্রথম উদাহরণে, কন্টেইনারটি আকারে বৃদ্ধি পাওয়ার সাথে সাথে টেক্সটটি প্রবেশের সাথে সাথে রিফ্লো হয়। দ্বিতীয় উদাহরণে টেক্সটটি বৃদ্ধি পাওয়ার সাথে সাথে রিফ্লো হয় না। Modifier.skipToLookaheadSize() যোগ করলে রিফ্লো বৃদ্ধির সাথে সাথে বাধা পায়।

কোন Modifier.skipToLookaheadSize() নেই - লক্ষ্য করুন "লোরেম ইপসাম" টেক্সট রিফ্লো হচ্ছে

Modifier.skipToLookaheadSize() - লক্ষ্য করুন যে "Lorem Ipsum" লেখাটি অ্যানিমেশনের শুরুতে তার চূড়ান্ত অবস্থা ধরে রেখেছে।

ক্লিপ এবং ওভারলে

বিভিন্ন কম্পোজেবলের মধ্যে শেয়ার করা এলিমেন্ট শেয়ার করার জন্য, যখন ট্রানজিশনটি গন্তব্যস্থলে তার মিলের সাথে শুরু হয় তখন কম্পোজেবলের রেন্ডারিং একটি লেয়ার ওভারলেতে উন্নীত হয় । এর প্রভাব হল এটি প্যারেন্টের সীমানা এবং এর লেয়ার রূপান্তর (উদাহরণস্বরূপ, আলফা এবং স্কেল) এড়িয়ে যাবে।

এটি অন্যান্য নন-শেয়ার্ড UI এলিমেন্টের উপরে রেন্ডার করবে। ট্রানজিশন শেষ হয়ে গেলে, এলিমেন্টটি ওভারলে থেকে তার নিজস্ব 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() থেকে ক্লিপ পাথ ব্যবহার করে।

শেয়ার্ড এলিমেন্ট ট্রানজিশনের সময় নির্দিষ্ট UI এলিমেন্ট, যেমন বটম বার বা ফ্লোটিং অ্যাকশন বোতাম, সবসময় উপরে রাখার জন্য, 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

(লক্ষ্য করুন যে তালিকার অন্যান্য আইটেমগুলি কীভাবে একটি আইটেমের বৃদ্ধির প্রতিক্রিয়ায় নীচের দিকে নেমে যায়)