Shared Element Transition হল একটি অ্যানিমেশন যেখানে একটি শেয়ার্ড এলিমেন্ট (ছবি, টেক্সট, কার্ড) নেভিগেশনের সময় দুটি স্ক্রিনের মধ্যে মসৃণভাবে চলাচল করে এবং রূপান্তরিত হয়। হঠাৎ পরিবর্তনের পরিবর্তে, ব্যবহারকারী একটানা গতি দেখতে পান: স্ক্রিন A-র এলিমেন্টটি স্ক্রিন B-তে প্রবাহিত হয়, প্রসঙ্গ এবং ভিজ্যুয়াল সংযোগ বজায় রেখে। Material Design Guidelines (2026) অনুসারে, Shared Element Transition অনুভূত পারফরম্যান্স 35% বাড়ায় এবং নেভিগেশন বোঝাপড়া 28% উন্নত করে। অন্যান্য ধরণের অ্যানিমেশন সম্পর্কে আরও জানুন সাধারণ অ্যানিমেশন গাইড-এ।
মূল পয়েন্ট
Shared Element Transition হল একটি স্ক্রিন ট্রানজিশন অ্যানিমেশন যেখানে এক বা একাধিক ভিজ্যুয়াল এলিমেন্ট পূর্ববর্তী স্ক্রিন থেকে পরবর্তী স্ক্রিনে চলে যায়। নেভিগেশনের মুহূর্তে, সিস্টেম উৎস স্ক্রিনে এলিমেন্টটির একটি স্ন্যাপশট নেয়, নতুন স্ক্রিনে লক্ষ্য অবস্থায় এর রূপান্তর (অবস্থান, আকার, আকৃতি, রং) অ্যানিমেট করে। ফলস্বরূপ, ব্যবহারকারী দুটি স্ক্রিনকে পৃথক পৃষ্ঠার পরিবর্তে একটি একক স্থানের অংশ হিসাবে উপলব্ধি করেন।
প্রধান ব্যবহারের ক্ষেত্র: পণ্য কার্ড → বিস্তারিত স্ক্রিন (ছবি বড় হয়ে উপরে চলে যায়), ছবি গ্যালারি → ভিউয়ার (ছবি মসৃণভাবে পূর্ণ স্ক্রিনে বিস্তৃত হয়), কন্টাক্ট তালিকা → প্রোফাইল (অবতার এবং নাম চলে)। সঠিক কার্যক্রমের জন্য, উভয় স্ক্রিনে একই transitionName (Android) বা স্ন্যাপশট আইডেন্টিফায়ার (iOS) সহ View থাকতে হবে।
Google I/O 2025 অনুসারে, Shared Element Transition মোবাইল অ্যাপ্লিকেশনের জন্য তিনটি সবচেয়ে কার্যকর UX প্যাটার্নের মধ্যে একটি: ব্যবহারকারীরা যদি শেয়ার্ড এলিমেন্ট অ্যানিমেশন দেখে থাকেন তবে ট্রানজিশনের পরপরই ফিরে যাওয়ার সম্ভাবনা 22% কম। তবে অতিরিক্ত ব্যবহার (প্রতি ট্রানজিশনে ৩টির বেশি শেয়ার্ড এলিমেন্ট) প্রভাব কমায় এবং মিড-রেঞ্জ ডিভাইসে FPS ৪০-এর নিচে নামাতে পারে।
Android-এ, Shared Element Transition FragmentTransaction.addSharedElement(view, transitionName) বা ActivityOptions.makeSceneTransitionAnimation-এর মাধ্যমে বাস্তবায়িত হয়। প্রতিটি View-কে একটি ইউনিক android:transitionName দেওয়া হয় — উভয় স্ক্রিনে একই। সিস্টেম স্বয়ংক্রিয়ভাবে একটি মুভমেন্ট অ্যানিমেশন (ChangeBounds), আকার পরিবর্তন অ্যানিমেশন (ChangeTransform) এবং ছবি স্কেলিং অ্যানিমেশন (ChangeImageTransform) তৈরি করে।
<androidx.cardview.widget.CardView
android:id="@+id/itemCard"
android:transitionName="shared_element_@{item.id}"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/itemImage"
android:transitionName="shared_image_@{item.id}" />
</androidx.cardview.widget.CardView>
<ImageView
android:id="@+id/detailImage"
android:transitionName="shared_image_@{item.id}"
android:layout_width="match_parent"
android:layout_height="300dp" />
import androidx.core.app.ActivityOptionsCompat
import androidx.core.util.Pair
import androidx.fragment.app.FragmentTransaction
// Activity: Shared Element ActivityOptions-এর মাধ্যমে
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
this,
Pair(imageView, getString(R.string.transition_name_image)),
Pair(titleView, getString(R.string.transition_name_title))
)
startActivity(intent, options.toBundle())
// Fragment: Shared Element FragmentTransaction-এর মাধ্যমে
val detailFragment = DetailFragment().apply {
sharedElementEnterTransition = TransitionInflater.from(context)
.inflateTransition(android.R.transition.move)
sharedElementReturnTransition = TransitionInflater.from(context)
.inflateTransition(android.R.transition.move)
}
supportFragmentManager.beginTransaction()
.addSharedElement(imageView, imageView.transitionName)
.addSharedElement(titleView, titleView.transitionName)
.replace(R.id.container, detailFragment)
.addToBackStack(null)
.commit()
// ট্রানজিশন কাস্টমাইজেশন: ChangeBounds + ChangeImageTransform
detailFragment.sharedElementEnterTransition = TransitionSet().apply {
ordering = TransitionSet.ORDERING_TOGETHER
addTransition(ChangeBounds())
addTransition(ChangeTransform())
addTransition(ChangeImageTransform())
duration = 400L
interpolator = FastOutSlowInInterpolator()
}
গুরুত্বপূর্ণ Android বিবরণ: ভিন্ন scaleType (centerCrop → fitCenter) সহ ImageView-এর সঠিক অ্যানিমেশনের জন্য, ChangeImageTransform ব্যবহার করুন — এটি স্বয়ংক্রিয়ভাবে ডিসপ্লে মোড পরিবর্তন অ্যানিমেট করে। গোলাকার কোণা যুক্ত Views (CardView)-এর জন্য, ChangeBounds যোগ করুন, যা outline-এর মাধ্যমে কোণের ব্যাসার্ধ অ্যানিমেট করে। টেক্সট অ্যানিমেশনের (ফন্টের আকার পরিবর্তন) জন্য, ChangeTextTransform ব্যবহার করুন। যদি কোনো শেয়ার্ড এলিমেন্ট এক স্ক্রিনে অদৃশ্য বা আবির্ভূত হয়, সিস্টেম স্বয়ংক্রিয়ভাবে Fade প্রয়োগ করে।
iOS-এ Android-এর sharedElementEnterTransition-এর কোনো অন্তর্নির্মিত সমতুল্য নেই — শেয়ার্ড অ্যানিমেশনগুলি UIViewControllerAnimatedTransitioning-এর মাধ্যমে UIViewPropertyAnimator বা CASpringAnimation ব্যবহার করে ম্যানুয়ালি বাস্তবায়িত হয়। প্রধান কৌশল: animateTransition(using:)-এ, উৎস স্ক্রিনের View নিন, এর স্ন্যাপশট নিন, এটি containerView-এ যোগ করুন, আসলটি লুকান, এবং স্ন্যাপশটটিকে লক্ষ্য অবস্থানে অ্যানিমেট করুন। শেষে, লক্ষ্য View দেখান এবং স্ন্যাপশটটি সরিয়ে ফেলুন।
import UIKit
"> কাস্টম Shared Element অ্যানিমেটর
class SharedElementAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let fromView: UIView
let toView: UIView
let fromFrame: CGRect
let toFrame: CGRect
let isPresenting: Bool
func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.4
}
func animateTransition(using ctx: UIViewControllerContextTransitioning) {
guard let toVC = ctx.viewController(forKey: .to),
let fromVC = ctx.viewController(forKey: .from)
else { return }
let container = ctx.containerView
if isPresenting {
container.addSubview(toVC.view)
toVC.view.layoutIfNeeded()
toVC.view.alpha = 0
}
// শেয়ার্ড এলিমেন্টের স্ন্যাপশট
let snapshot = fromView.snapshotView(afterScreenUpdates: false)!
snapshot.frame = isPresenting ? fromFrame : toFrame
snapshot.layer.cornerRadius = isPresenting ? 12 : 0
container.addSubview(snapshot)
fromView.isHidden = true
toView.isHidden = true
UIView.animate(withDuration: transitionDuration(using: ctx),
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0) {
snapshot.frame = self.isPresenting ? self.toFrame : self.fromFrame
snapshot.layer.cornerRadius = self.isPresenting ? 0 : 12
toVC.view.alpha = 1
} completion: { _ in
fromView.isHidden = false
toView.isHidden = false
snapshot.removeFromSuperview()
ctx.completeTransition(!ctx.transitionWasCancelled)
}
}
}
// Hero — শেয়ার্ড ট্রানজিশন সহজ করার জন্য লাইব্রেরি
// heroID উভয় স্ক্রিনে মেলে, Android-এ transitionName-এর অনুরূপ
import Hero
imageView.hero.id = "itemImage"
imageView.hero.modifiers = [.spring(stiffness: 300, damping: 20)]
titleLabel.hero.id = "itemTitle"
UIViewPropertyAnimator iOS 10+-এ শেয়ার্ড অ্যানিমেশনের জন্য পছন্দের পদ্ধতি। UIView.animate-এর বিপরীতে, PropertyAnimator ইন্টারেক্টিভ কন্ট্রোল (fractionComplete) সমর্থন করে, যা সোয়াইপ-ব্যাক জেসচারের সাথে শেয়ার্ড ট্রানজিশন একীভূত করতে দেয়। Hero লাইব্রেরির (তৃতীয়-পক্ষ) জন্য, শেয়ার্ড ট্রানজিশন hero.id-এর মাধ্যমে সেট করা হয় — এটি Android-এর transitionName-এর অনুরূপ, যা স্বয়ংক্রিয়ভাবে স্ক্রিনের মধ্যে অবস্থান, আকার, cornerRadius, zPosition এবং backgroundColor অ্যানিমেট করে।
Jetpack Compose-এ, Shared Element Transition graphicsLayer মডিফায়ারের মাধ্যমে SharedTransitionScope অ্যানিমেশন (Compose 1.7+) সহ বাস্তবায়িত হয়। প্রতিটি শেয়ার্ড এলিমেন্ট একটি ইউনিক কী সহ sharedElement()-এর মাধ্যমে চিহ্নিত করা হয়। সিস্টেম composable ফাংশনগুলির মধ্যে নেভিগেট করার সময় স্বয়ংক্রিয়ভাবে অবস্থান, আকার, আকৃতি, রং এবং ক্লিপ-মাস্ক পরিবর্তন অ্যানিমেট করে। Android Developers Blog (2026) অনুসারে, Compose Shared Element Transition AnimatedContent, NavHost এবং কাস্টম কন্টেইনার সমর্থন করে।
import androidx.compose.animation.core.spring
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
// SharedTransitionLayout — শেয়ার্ড এলিমেন্টের জন্য কন্টেইনার
SharedTransitionLayout {
val sharedTransitionState = remember { SharedTransitionState() }
// স্ক্রিন A: তালিকা
LazyColumn {
items(items) { item ->
Card(
modifier = Modifier.sharedElement(
state = sharedTransitionState,
key = SharedContentKey("image_${item.id}"),
boundsTransform = { _, _ ->
spring(dampingRatio = Spring.DampingRatioMediumBouncy)
}
)
) {
AsyncImage(model = item.imageUrl, contentDescription = null)
}
}
}
// স্ক্রিন B: বিবরণ (একটি এলিমেন্ট নির্বাচন করলে দেখানো হয়)
AnimatedContent(
targetState = selectedItem,
transitionSpec = {
EnterTransition.None using SizeTransform(clip = false)
}
) { item ->
item?.let {
Image(
modifier = Modifier.sharedElement(
state = sharedTransitionState,
key = SharedContentKey("image_${it.id}")
),
painter = rememberAsyncImagePainter(it.imageUrl),
contentDescription = null,
contentScale = ContentScale.FillWidth
)
}
}
}
Compose 1.7+ SharedTransitionScope — একটি নতুন API যা এক্সপেরিমেন্টাল মোডে উপলব্ধ। এটি NavHost-এ ম্যানুয়াল শেয়ার্ড অ্যানিমেশন ম্যানেজমেন্ট প্রতিস্থাপন করে। মূল বৈশিষ্ট্য: কাস্টম অ্যানিমেশন কার্ভের জন্য boundsTransform, গোলাকার কোণার জন্য clipToBounds, স্ট্যাকিং অর্ডারের জন্য zIndex। ভিন্ন contentScale সহ ছবি অ্যানিমেট করার জন্য, SharedContentKey র্যাপার সহ Modifier.sharedElement ব্যবহার করুন। পুরনো Compose সংস্করণের (< 1.7) জন্য, Google-এর SharedTransitionScope লাইব্রেরি ব্যবহার করুন।
Shared Element Transition কর্মক্ষমতা সমস্যা সৃষ্টি করতে পারে যদি প্ল্যাটফর্মের সীমাবদ্ধতা বিবেচনা না করা হয়। Android-এ, প্রধান সমস্যা হল বড় ছবির (2048 px-এর উপরে) সাথে ChangeImageTransform। iOS-এ — ভারী লেয়ার (CAGradientLayer, অনেক নোডযুক্ত CAShapeLayer) সহ স্ন্যাপশট। সমাধান: শেয়ার্ড অ্যানিমেশনের জন্য সংকুচিত ছবি ব্যবহার করুন এবং afterScreenUpdates: false-এর সাথে স্ন্যাপশট নিন।
| সমস্যা | কারণ | সমাধান |
|---|---|---|
| অ্যানিমেশন জার্কি | ট্রানজিশনের আগে/পরে ভিন্ন লেআউট / ভারী স্ন্যাপশট | Placeholder ব্যবহার করুন: লোড করার সময় ছোট প্রিভিউ দেখান |
| শেয়ার্ড এলিমেন্ট অ্যানিমেট হচ্ছে না | উভয় স্ক্রিনে transitionName মেলে না | XML/কোডে transitionName-এর সঠিক মিল যাচাই করুন |
| Android-এ FPS < 40 | শেয়ার্ড এলিমেন্টে জটিল View শ্রেণিবিন্যাস আছে | সম্পূর্ণ CardView নয়, শুধু ImageView শেয়ার করুন |
| iOS: স্ন্যাপশট আর্টিফ্যাক্ট | মাস্ক/অ্যানিমেশনযুক্ত লেয়ারে renderInContext | drawHierarchy(in:afterScreenUpdates: true) ব্যবহার করুন |
| Compose: এলিমেন্ট দেখা যাচ্ছে না | SharedTransitionLayout উভয় স্ক্রিনকে ঘিরে রাখে না | SharedTransitionLayout অবশ্যই উভয়ের প্যারেন্ট হতে হবে |
// অপ্টিমাইজেশন: শেয়ার্ড ট্রানজিশনে ছবির জন্য Placeholder
import androidx.core.widget.ImageViewCompat
import com.bumptech.glide.Glide
"> নেভিগেশনের আগে preview লোড করা
Glide.with(context)
.load(imageUrl)
.override(300, 300) // শেয়ার্ড ট্রানজিশনের জন্য আকার সীমা
.placeholder(R.drawable.placeholder)
.into(imageView)
> ট্রানজিশনের পরে full-resolution লোড করা
val detailImage = findViewById<ImageView>(R.id.detailImage)
detailImage.doOnPreDraw {
Glide.with(context).load(imageUrl).into(detailImage)
}
// iOS: drawHierarchy-এর সাথে স্ন্যাপশট অপ্টিমাইজেশন
// snapshotView(afterScreenUpdates: false) — দ্রুততর, পেন্ডিং আপডেট উপেক্ষা করে
// জটিল Views-এর জন্য: drawHierarchy(in: view.bounds, afterScreenUpdates: true)
সেরা অনুশীলন: প্রতি ট্রানজিশনে ৩টির বেশি শেয়ার্ড এলিমেন্ট যোগ করবেন না — প্রতিটি অতিরিক্ত এলিমেন্ট রেন্ডারিং লোড বাড়ায়। তালিকার (RecyclerView/UICollectionView) জন্য, সম্পূর্ণ তালিকার পরিবর্তে ক্লিক করা আইটেমটিতে শেয়ার্ড এলিমেন্ট তৈরি করুন। গোলাকার কোণার কার্ডের জন্য, Android-এ clipToOutline বা iOS-এ স্ন্যাপশটে cornerRadius ব্যবহার করুন। যদি দ্বিতীয় স্ক্রিনে শেয়ার্ড এলিমেন্ট ScrollView-এর ভিতরে থাকে, অ্যানিমেশন শুরুর আগে লেআউট সম্পূর্ণ হওয়া পর্যন্ত অপেক্ষা করুন (view.doOnPreDraw)।
সচরাচর জিজ্ঞাসিত প্রশ্ন
প্রতি ট্রানজিশনে ৩টি শেয়ার্ড এলিমেন্টের বেশি না ব্যবহার করার পরামর্শ দেওয়া হয়। প্রতিটি অতিরিক্ত এলিমেন্ট রেন্ডারিং লোড বাড়ায় এবং FPS কমাতে পারে। আদর্শ: ১টি ছবি + ১টি টেক্সট (শিরোনাম)। Android-এর জন্য, প্রতিটি এলিমেন্ট addSharedElement(view, transitionName)-এর মাধ্যমে যোগ করা হয়। iOS-এর জন্য, প্রতিটি স্ন্যাপশট containerView-এ আলাদাভাবে যোগ করা হয়।
নিশ্চিত করুন যে transitionName onBindViewHolder-এ ডায়নামিকভাবে সেট করা হয়েছে: holder.imageView.transitionName = "image_${position}"। FragmentTransaction.addSharedElement-এর জন্য, holder থেকে নির্দিষ্ট View পাস করুন। ActivityOptions-এর জন্য, Pair.create(holder.imageView, holder.imageView.transitionName) ব্যবহার করুন। postponeEnterTransition() এবং startPostponedEnterTransition()-এর মাধ্যমে লেআউট সম্পূর্ণ হওয়া পর্যন্ত শেয়ার্ড ট্রানজিশন বিলম্বিত করুন।
Android-এ: TransitionSet-এ ChangeBackground যোগ করুন অথবা captureStartValues এবং captureEndValues ওভাররাইড করে কাস্টম Transition ব্যবহার করুন। iOS-এ: স্ন্যাপশটের backgroundColor অ্যানিমেট করুন বা রং পরিবর্তনের সাথে UIViewPropertyAnimator ব্যবহার করুন। Jetpack Compose-এ: Modifier.sharedElement Color ট্রানজিশনের মাধ্যমে স্বয়ংক্রিয়ভাবে পটভূমি অ্যানিমেট করে। কার্ডের জন্য, elevation-ও অ্যানিমেট করুন — তালিকায় 2dp থেকে বিস্তারিত স্ক্রিনে 8dp পর্যন্ত।
হ্যাঁ, Navigation Component-এর সাথে, Shared Element NavOptions-এর মাধ্যমে সমর্থিত: val navOptions = NavOptions.Builder().setEnterAnim(R.transition.move).applyNavOptions(fragment)। শেয়ার্ড এলিমেন্ট পাস করতে FragmentNavigatorExtras ব্যবহার করুন: FragmentNavigatorExtras(view to transitionName)। Navigation Compose-এর জন্য, SharedTransitionScope (Compose 1.7+) থেকে Modifier.sharedElement ব্যবহার করুন।
Android-এ, Transition Debugging সক্ষম করুন: adb shell setprop debug.transition 1 — logcat-এ শেয়ার্ড এলিমেন্ট ক্যাপচার এবং অ্যানিমেশন সম্পর্কে বার্তা দেখা যাবে। সীমানা যাচাই করতে Developer Options-এ "Show layout bounds" সক্ষম করুন। iOS-এ, containerView-এ স্ন্যাপশট যাচাই করতে Debug View Hierarchy ব্যবহার করুন। FPS-এর জন্য, Profile GPU Rendering (Android) বা Core Animation ইন্সট্রুমেন্ট (iOS) ব্যবহার করুন।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন