compose低版本仿官方共享元素过渡动画的实现

在官方文档中得知,Compose 1.7.0-beta01 提供共享元素支持,并且 处于实验阶段,这些 API 将来可能会发生变化。但并不是所有人的版本都与之适配,于是在这里我将实现一个差不多效果的过渡动画。

实现思路:

首先定义数据模型和数据来源,这里我给几个假数据

object people{
    val people = listOf(
        StudyRoom(
            imageRes = R.drawable.room2,
            name = "共享自习室",
            description = "人数:520"
        ),
        StudyRoom(
            imageRes = R.drawable.room1,
            name = "备考1",
            description = "人数:120"
        ),
        StudyRoom(
            imageRes = R.drawable.room3,
            name = "备考2",
            description = "人数:200"
        )
    )
}

data class StudyRoom(
    val imageRes: Int,
    val name: String,
    val description: String
)

第二步,定义普通卡片和详细卡片内容(也可以根据我的思路自行更改为screen与screen之间的转换)

@Composable
fun PersonCard(
    person: StudyRoom,
    onClick: () -> Unit,
    isVisible: Boolean,
    modifier: Modifier = Modifier
) {
    SharedTransitionLayout(isVisible = isVisible, modifier = modifier) {
        Card(
            modifier = Modifier
                .width(300.dp)
                .height(180.dp)
                .clickable { onClick() },
            elevation = CardDefaults.cardElevation(defaultElevation = 0.dp), 
            colors = CardDefaults.cardColors(containerColor = Color.Transparent)
        ) {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(180.dp)
            ) {
                // 背景图片
                Image(
                    painter = painterResource(id = person.imageRes),
                    contentDescription = null,
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(200.dp),
                    contentScale = ContentScale.Crop
                )
                // 叠加层:标题和人数
                Column(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(16.dp),
                    verticalArrangement = Arrangement.Bottom,
                    horizontalAlignment = Alignment.Start
                ) {
                    Text(
                        text = person.name,
                        color = Color.White,
                        fontSize = 18.sp,
                        style = typography.titleMedium,
                        modifier = Modifier
                            .background(Color(0x80000000)) // 半透明
                            .padding(4.dp)
                    )
                    Text(
                        text = person.description,
                        color = Color.White,
                        fontSize = 14.sp,
                        style = typography.bodySmall,
                        modifier = Modifier
                            .background(Color(0x80000000)) // 半透明
                            .padding(4.dp)
                    )
                }
            }
        }


    }
}

@Composable
fun PersonDetailsCard(
    person: StudyRoom,
    isVisible: Boolean,
    onClose: () -> Unit
) {
    SharedTransitionLayout(isVisible = isVisible) {
        Card(
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp)
                .clickable { onClose() },
            elevation = CardDefaults.cardElevation(4.dp),
            colors = CardDefaults.cardColors(containerColor = Color.White)
        ) {
            Column(
                modifier = Modifier.padding(16.dp)
            ) {
                Image(
                    painter = painterResource(id = person.imageRes),
                    contentDescription = null,
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(200.dp),
                    contentScale = ContentScale.Crop
                )
                Spacer(modifier = Modifier.height(16.dp))
                Text(
                    text = person.name,
                    color = Color.Black,
                    fontSize = 24.sp,
                    modifier = Modifier.padding(bottom = 4.dp),
                    style =  typography.titleMedium
                )
                Text(
                    text = person.description,
                    color = Color.Gray,
                    fontSize = 16.sp,
                    style =  typography.bodySmall
                )
            }
        }
    }
}

第三步,自定义动画布局,用于实现显示与隐藏的过渡动画

@OptIn(ExperimentalAnimationApi::class)
@Composable
fun SharedTransitionLayout(
    isVisible: Boolean,
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    val animationDuration = 600

    AnimatedVisibility(
        visible = isVisible,
        enter = fadeIn(animationSpec = tween(durationMillis = animationDuration, easing = EaseInOutEasing)) +
                expandVertically(animationSpec = tween(durationMillis = animationDuration, easing = EaseInOutEasing)),
        exit = fadeOut(animationSpec = tween(durationMillis = animationDuration, easing = EaseInOutEasing)) +
                shrinkVertically(animationSpec = tween(durationMillis = animationDuration, easing = EaseInOutEasing)),
        modifier = modifier
    ) {
        Box(
            modifier = Modifier.animateContentSize(
                animationSpec = tween(
                    durationMillis = animationDuration,
                    easing = EaseInOutEasing
                )
            )
        ) {
            content()
        }
    }
}

注意:不要漏了全局变量,用于控制动画的缓动效果。

private val EaseInOutEasing = CubicBezierEasing(0.42f, 0f, 0.58f, 1f)

项目地址: 

 simonniex/SharedAnimation: compose低版本仿官方共享元素过渡动画的实现 (github.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值