Shared Element Transition — wat is het, gedeelde elementen in overgangen

Auteur: IT Sectr Gepubliceerd: 2026-03-02 Leestijd: 10 min

Shared Element Transition — een animatie waarbij een gedeeld element (afbeelding, tekst, kaart) soepel beweegt en transformeert tussen twee schermen tijdens navigatie. In plaats van een abrupte overgang ziet de gebruiker een continue beweging: het element van scherm A «stroomt» naar scherm B, met behoud van context en visuele verbinding. Volgens Material Design Guidelines (2026) verhoogt Shared Element Transition de waargenomen prestaties met 35% en verbetert het begrip van navigatie met 28%. Lees meer over andere soorten animaties in de algemene animatiegids.

Belangrijkste punten

  • Shared Element Transition — animatie van een gedeeld element tussen twee schermen tijdens navigatie.
  • sharedElementEnterTransition — XML-attribuut in Android voor het configureren van de binnenkomst van het gedeelde element.
  • UIViewPropertyAnimator — basis voor aangepaste gedeelde animaties tussen UIViewController in iOS.
  • Het gedeelde element wordt ingesteld via transitionName in Android en tag of snapshot in iOS.
  • Animaties werken met ImageView, TextView, CardView, Button en elke View met een unieke identificatie.

Wat is Shared Element Transition?

Shared Element Transition is een animatie voor het wisselen van schermen waarbij een of meer visuele elementen van het vorige scherm naar het volgende «verhuizen». Op het moment van navigatie maakt het systeem een snapshot van het element op het bronscherm, animeert de transformatie (positie, grootte, vorm, kleur) naar de doeltoestand op het nieuwe scherm. Hierdoor ervaart de gebruiker twee schermen als delen van één ruimte, niet als afzonderlijke pagina's.

Belangrijkste gebruiksscenario's: productkaart → detailscherm (afbeelding wordt groter en gaat naar boven), afbeeldingengalerij → weergave (afbeelding opent soepel naar volledig scherm), contactenlijst → profiel (avatar en naam verplaatsen zich). Voor correcte werking moeten beide schermen een View hebben met dezelfde transitionName (Android) of snapshot-identificatie (iOS).

Volgens Google I/O 2025 is Shared Element Transition een van de drie meest effectieve UX-patronen voor mobiele apps: gebruikers keren 22% minder vaak direct terug na een overgang als ze de animatie van het gedeelde element hebben gezien. Overmatig gebruik (meer dan 3 gedeelde elementen per overgang) vermindert echter het effect en kan een FPS-daling onder 40 veroorzaken op middenklasse apparaten.

Shared Element Transition in Android (Kotlin)

In Android wordt Shared Element Transition geïmplementeerd via FragmentTransaction.addSharedElement(view, transitionName) of ActivityOptions.makeSceneTransitionAnimation. Elke View krijgt een unieke android:transitionName — dezelfde op beide schermen. Het systeem maakt automatisch een animatie voor verplaatsing (ChangeBounds), grootteverandering (ChangeTransform) en beeld schalen (ChangeImageTransform).

xml

<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" />
kotlin
import androidx.core.app.ActivityOptionsCompat
import androidx.core.util.Pair
import androidx.fragment.app.FragmentTransaction

// Activity: Shared Element via 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 via 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()

// Overgang aanpassen: ChangeBounds + ChangeImageTransform
detailFragment.sharedElementEnterTransition = TransitionSet().apply {
    ordering = TransitionSet.ORDERING_TOGETHER
    addTransition(ChangeBounds())
    addTransition(ChangeTransform())
    addTransition(ChangeImageTransform())
    duration = 400L
    interpolator = FastOutSlowInInterpolator()
}

Belangrijke Android-details: gebruik ChangeImageTransform voor correcte animatie van ImageView met verschillende scaleType (centerCrop → fitCenter) — het animeert automatisch de verandering van weergavemethode. Voor View met afgeronde hoeken (CardView) voeg ChangeBounds toe, die de hoekradius animeert via outline. Gebruik ChangeTextTransform voor tekst animatie (lettergrootte verandering). Als het gedeelde element verdwijnt of verschijnt op een van de schermen — past het systeem automatisch Fade toe.

Shared Element Transition in iOS (Swift)

iOS heeft geen ingebouwd equivalent van Android's sharedElementEnterTransition — gedeelde animaties worden handmatig geïmplementeerd via UIViewControllerAnimatedTransitioning met UIViewPropertyAnimator of CASpringAnimation. Hoofdtechniek: in animateTransition(using:) neem de View van het bronscherm, maak een snapshot, voeg deze toe aan containerView, verberg het origineel en animeer de snapshot naar de doelpositie. Aan het einde toon je de doel-View en verwijder je de snapshot.

swift
import UIKit

"> Aangepaste Shared Element-animator
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
        }

        // Snapshot van het gedeelde element
        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 — bibliotheek voor het vereenvoudigen van shared transitions
// heroID komt overeen op beide schermen, analoog aan transitionName in Android
import Hero

imageView.hero.id = "itemImage"
imageView.hero.modifiers = [.spring(stiffness: 300, damping: 20)]
titleLabel.hero.id = "itemTitle"

UIViewPropertyAnimator — de voorkeursmethode voor gedeelde animaties op iOS 10+. In tegenstelling tot UIView.animate ondersteunt PropertyAnimator interactieve controle (fractionComplete), waardoor shared transition kan worden geïntegreerd met de swipe-back gebaar. Voor de Hero-bibliotheek (derde partij) wordt shared transition ingesteld via hero.id — dit is de analogie van transitionName uit Android, die automatisch positie, grootte, cornerRadius, zPosition en backgroundColor tussen schermen animeert.

Shared Element in Jetpack Compose

In Jetpack Compose wordt Shared Element Transition geïmplementeerd via de modifier graphicsLayer met SharedTransitionScope-animatie (Compose 1.7+). Elk gedeeld element wordt gemarkeerd via sharedElement() met een unieke sleutel. Het systeem animeert automatisch de verandering van positie, grootte, vorm, kleur en bijsnijdmasker tijdens navigatie tussen composable-functies. Volgens Android Developers Blog (2026) ondersteunt Compose Shared Element Transition AnimatedContent, NavHost en aangepaste containers.

kotlin
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 — container voor gedeelde elementen
SharedTransitionLayout {
    val sharedTransitionState = remember { SharedTransitionState() }

    // Scherm A: lijst
    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)
            }
        }
    }

    // Scherm B: details (wordt getoond bij selectie van element)
    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 — nieuwe API beschikbaar in experimentele modus. Vervangt handmatig beheer van gedeelde animaties in NavHost. Belangrijkste mogelijkheden: boundsTransform voor aangepaste animatiecurve, clipToBounds voor afgeronde hoeken, zIndex voor stapelingsvolgorde. Gebruik Modifier.sharedElement met SharedContentKey wrapper voor animatie van afbeeldingen met verschillende contentScale. Gebruik Google's SharedTransitionScope-bibliotheek voor ondersteuning van oudere Compose-versies (< 1.7).

Optimalisatie en typische problemen

Shared Element Transition kan prestatieproblemen veroorzaken als er geen rekening wordt gehouden met platformbeperkingen. In Android is het grootste probleem ChangeImageTransform met grote afbeeldingen (boven 2048 px). In iOS — snapshot met zware lagen (CAGradientLayer, CAShapeLayer met veel knooppunten). Oplossing: gebruik gecomprimeerde afbeeldingen voor gedeelde animaties en maak snapshots met afterScreenUpdates: false.

ProbleemOorzaakOplossing
Schokkerige animatieVerschillende layout voor/na overgang / zware snapshotGebruik Placeholder: toon kleine preview tijdens laden
Gedeeld element animeert niettransitionName komt niet overeen op beide schermenControleer exacte match van transitionName in XML/code
FPS < 40 op AndroidGedeeld element bevat complexe View-hiërarchieDeel alleen ImageView, niet de hele CardView
iOS: snapshot-artefactenrenderInContext op laag met masks/animatiesGebruik drawHierarchy(in:afterScreenUpdates: true)
Compose: element verschijnt nietSharedTransitionLayout omvat niet beide schermenSharedTransitionLayout moet ouder zijn voor beide
kotlin
// Optimalisatie: Placeholder voor afbeelding in shared transition
import androidx.core.widget.ImageViewCompat
import com.bumptech.glide.Glide

"> Preview laden vóór de navigatie
Glide.with(context)
    .load(imageUrl)
    .override(300, 300) // Groottebeperking voor shared transition
    .placeholder(R.drawable.placeholder)
    .into(imageView)

> Uitgesteld laden van full-resolution na de overgang
val detailImage = findViewById<ImageView>(R.id.detailImage)
detailImage.doOnPreDraw {
    Glide.with(context).load(imageUrl).into(detailImage)
}

// iOS: optimalisatie van snapshot met drawHierarchy
// snapshotView(afterScreenUpdates: false) — sneller, negeert lopende updates
// Voor complexe Views: drawHierarchy(in: view.bounds, afterScreenUpdates: true)

Beste praktijken: voeg niet meer dan 3 gedeelde elementen per overgang toe — elk extra element verhoogt de renderbelasting. Voor lijsten (RecyclerView/UICollectionView) maak het gedeelde element op het geklikte item, niet op de hele lijst. Gebruik clipToOutline op Android of cornerRadius in snapshot op iOS voor kaarten met afgeronde hoeken. Als het gedeelde element op het tweede scherm in een ScrollView staat, wacht dan op voltooiing van de layout (view.doOnPreDraw) voordat de animatie start.

Veelgestelde vragen

Hoeveel gedeelde elementen kunnen in één overgang worden geanimeerd?

Niet meer dan 3 gedeelde elementen per overgang wordt aanbevolen. Elk extra element verhoogt de renderbelasting en kan FPS-daling veroorzaken. Optimaal: 1 afbeelding + 1 tekst (titel). Voor Android wordt elk element toegevoegd via addSharedElement(view, transitionName). Voor iOS wordt elke snapshot afzonderlijk aan containerView toegevoegd.

Shared Element Transition werkt niet met RecyclerView — wat te doen?

Zorg ervoor dat transitionName dynamisch is ingesteld in onBindViewHolder: holder.imageView.transitionName = «image_${position}». Geef voor FragmentTransaction.addSharedElement de specifieke View uit holder door. Gebruik voor ActivityOptions Pair.create(holder.imageView, holder.imageView.transitionName). Stel shared transition uit tot layout voltooid is via postponeEnterTransition() en startPostponedEnterTransition().

Hoe maak je Shared Element Transition met achtergrondkleurverandering?

In Android: voeg ChangeBackground toe aan TransitionSet of gebruik een aangepaste Transition die captureStartValues en captureEndValues overschrijft. In iOS: animeer backgroundColor van snapshot of UIViewPropertyAnimator met kleurverandering. In Jetpack Compose: Modifier.sharedElement animeert automatisch background via Color transition. Animeer ook elevation voor kaarten — van 2dp op lijst tot 8dp op detailscherm.

Werkt Shared Element op Android met Jetpack Navigation?

Ja, met Navigation Component wordt Shared Element ondersteund via NavOptions: val navOptions = NavOptions.Builder().setEnterAnim(R.transition.move).applyNavOptions(fragment). Gebruik FragmentNavigatorExtras voor het doorgeven van gedeelde elementen: FragmentNavigatorExtras(view to transitionName). Gebruik Modifier.sharedElement van SharedTransitionScope (Compose 1.7+) voor Navigation Compose.

Hoe debug je Shared Element Transition?

Schakel in Android Transition Debugging in: adb shell setprop debug.transition 1 — in logcat verschijnen berichten over vastlegging en animatie van gedeelde elementen. Schakel «Show layout bounds» in Developer Options in om grenzen voor/na te controleren. Gebruik in iOS Debug View Hierarchy om snapshot in containerView te controleren. Gebruik Profile GPU Rendering (Android) of Core Animation instrument (iOS) voor FPS-monitoring.

Samenvatting

  • Shared Element Transition — animatie van een gedeeld element tussen schermen met behoud van visuele context.
  • Android: FragmentTransaction.addSharedElement of ActivityOptions.makeSceneTransitionAnimation.
  • iOS: aangepaste implementatie via UIViewControllerAnimatedTransitioning + snapshot + PropertyAnimator.
  • Jetpack Compose: Modifier.sharedElement van SharedTransitionScope (Compose 1.7+).
  • Maximaal 3 gedeelde elementen per overgang om FPS-daling te voorkomen.
  • Voor ImageView met verschillende scaleType gebruik ChangeImageTransform (Android) of snapshot (iOS).
  • Hero bibliotheek (iOS) — oplossing van derde partij met hero.id, vergelijkbaar met transitionName in Android.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook