Menyesuaikan transisi elemen bersama

Untuk menyesuaikan cara animasi transisi elemen bersama berjalan, ada beberapa parameter yang dapat digunakan untuk mengubah cara transisi elemen bersama.

Spesifikasi animasi

Untuk mengubah spesifikasi animasi yang digunakan untuk ukuran dan gerakan posisi, Anda dapat tentukan parameter boundsTransform yang berbeda di Modifier.sharedElement(). Hal ini memberikan posisi Rect awal dan posisi Rect target.

Misalnya, untuk membuat teks dalam contoh sebelumnya bergerak dengan gerakan busur, tentukan parameter boundsTransform untuk menggunakan spesifikasi 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
    )
)

Anda dapat menggunakan AnimationSpec apa pun. Contoh ini menggunakan spesifikasi keyframes.

Gambar 1. Contoh yang menampilkan parameter boundsTransform yang berbeda

Mode ubah ukuran

Saat menganimasikan antara dua batas bersama, Anda dapat menetapkan parameter resizeMode ke RemeasureToBounds atau ScaleToBounds. Parameter ini menentukan cara transisi elemen bersama antara dua status. ScaleToBounds pertama kali mengukur tata letak turunan dengan batasan lookahead (atau target). Kemudian, tata letak stabil turunan diskalakan agar sesuai dengan batas bersama. ScaleToBounds dapat dianggap sebagai "skala grafis" di antara status.

Sedangkan RemeasureToBounds mengukur ulang dan menata ulang tata letak turunan sharedBounds dengan batasan tetap animasi berdasarkan ukuran target. Pengukuran ulang dipicu oleh perubahan ukuran batas, yang berpotensi menjadi setiap frame.

Untuk composable Text, ScaleToBounds direkomendasikan karena akan menghindari penataan ulang dan pengaliran ulang teks ke baris yang berbeda. Untuk batas yang merupakan aspek berbeda rasio aspek, dan jika Anda menginginkan kontinuitas yang mengalir di antara dua elemen bersama, RemeasureToBounds direkomendasikan.

Perbedaan antara kedua mode pengubahan ukuran dapat dilihat pada contoh berikut:

ScaleToBounds

RemeasureToBounds

Lewati ke tata letak akhir

Secara default, saat bertransisi di antara dua tata letak, ukuran tata letak akan dianimasikan antara status awal dan akhir. Ini mungkin perilaku yang tidak diinginkan ketika menganimasikan konten seperti teks.

Contoh berikut mengilustrasikan teks deskripsi "Lorem Ipsum" yang masuk ke layar dengan dua cara berbeda. Contoh pertama, teks berubah posisi/geometri dimasukkan saat kontainer bertambah besar, contoh kedua teks tidak menyesuaikan posisi/geometri seiring perkembangannya. Menambahkan Modifier.skipToLookaheadSize() akan mencegah pengisian ulang seiring pertumbuhannya.

Tidak ada Modifier.skipToLookahead() - perhatikan "Lorem Ipsum" perubahan posisi/geometri teks

Modifier.skipToLookahead() - perhatikan "Lorem Ipsum" teks mempertahankan status akhirnya di awal animasi

Klip dan overlay

Konsep penting saat membuat elemen bersama di Compose adalah agar elemen tersebut dapat dibagikan di antara composable yang berbeda, rendering composable akan ditingkatkan menjadi overlay lapisan saat transisi dimulai ke kecocokannya di tujuan. Efeknya adalah dia akan meng-escape batas induk dan transformasi lapisannya (misalnya alfa dan skala).

Elemen ini akan dirender di atas elemen UI non-bersama lainnya, setelah transisi selesai, elemen akan dihapus dari overlay ke DrawScope-nya sendiri.

Untuk memotong elemen bersama ke suatu bentuk, gunakan Modifier.clip() standar fungsi tersebut. Tempatkan setelah 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
)

Jika perlu memastikan bahwa elemen bersama tidak pernah dirender di luar penampung induk, Anda dapat menetapkan clipInOverlayDuringTransition di sharedElement(). Menurut default, untuk batas bersama bertingkat, clipInOverlayDuringTransition akan menggunakan klip jalur dari induk sharedBounds().

Untuk mendukung penyimpanan elemen UI tertentu, seperti panel bawah atau tindakan mengambang tombol, selalu di atas selama transisi elemen bersama, gunakan Modifier.renderInSharedTransitionScopeOverlay() Secara {i>default<i}, Pengubah mempertahankan konten dalam overlay selama waktu saat dibagikan aktif.

Misalnya, di Jetsnack, BottomAppBar harus ditempatkan di atas bersama hingga layar tidak terlihat. Menambahkan pengubah ke dalam composable dapat membuatnya tetap tinggi.

Tanpa Modifier.renderInSharedTransitionScopeOverlay()

Dengan Modifier.renderInSharedTransitionScopeOverlay()

Terkadang, Anda mungkin ingin composable non-bersama bergerak serta tetap berada di atas composable lain sebelum transisi. Dalam kasus semacam itu, gunakan renderInSharedTransitionScopeOverlay().animateEnterExit() untuk menganimasikan composable saat transisi elemen bersama berjalan:

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

Gambar 2.Panel Aplikasi bawah bergeser masuk dan keluar saat transisi animasi

Dalam kasus yang jarang terjadi, jika Anda ingin elemen bersama tidak dirender dalam overlay, Anda dapat menetapkan renderInOverlayDuringTransition pada sharedElement() ke salah (false).

Memberi tahu tata letak yang seinduk mengenai perubahan pada ukuran elemen bersama

Secara default, sharedBounds() dan sharedElement() tidak memberi tahu orang tua penampung dengan berbagai ukuran akan berubah saat transisi tata letak.

Untuk menyebarkan perubahan ukuran ke penampung induk saat bertransisi, ubah parameter placeHolderSize menjadi PlaceHolderSize.animatedSize. Melakukan sehingga menyebabkan item menjadi membesar atau menyusut. Semua item lain dalam tata letak merespons perubahan tersebut.

PlaceholderSize.contentSize (default)

PlaceholderSize.animatedSize

(Perhatikan bagaimana item lain dalam daftar bergerak ke bawah sebagai respons terhadap satu item yang bertambah)