Jetpack Compose实现Logo缩放过渡至登录页动画方案咨询
实现Jetpack Compose顺序缩放动画方案
方案一:使用Animatable+挂起函数控制顺序(最直观可控)
Animatable的animateTo是挂起函数,会等待动画完成后再执行后续代码,完美适配这种顺序执行的场景:
@Composable fun SplashLoginTransition() { // 控制组件显示状态 var showLogo by remember { mutableStateOf(true) } var showLogin by remember { mutableStateOf(false) } // 控制缩放动画的Animatable实例 val logoScale = remember { Animatable(0f) } val loginScale = remember { Animatable(0f) } LaunchedEffect(Unit) { // 1. Logo缩放入场(从0到1) logoScale.animateTo( targetValue = 1f, animationSpec = tween(durationMillis = 1000) ) // 可选:让Logo停留一段时间 delay(1000) // 2. Logo缩放退场(从1到0) logoScale.animateTo( targetValue = 0f, animationSpec = tween(durationMillis = 1000) ) // 3. 退场完成后切换状态,启动登录页入场动画 showLogo = false showLogin = true loginScale.animateTo( targetValue = 1f, animationSpec = tween(durationMillis = 1000) ) } Box(modifier = Modifier.fillMaxSize()) { if (showLogo) { Image( painter = painterResource(id = R.drawable.your_app_logo), contentDescription = "App Logo", modifier = Modifier .align(Alignment.Center) .scale(logoScale.value) ) } if (showLogin) { // 替换成你的登录页组件 LoginScreen( modifier = Modifier .fillMaxSize() .scale(loginScale.value) ) } } } // 示例登录页组件 @Composable fun LoginScreen(modifier: Modifier = Modifier) { Column( modifier = modifier.padding(16.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { TextField(value = "", onValueChange = {}, label = { Text("用户名") }) Spacer(modifier = Modifier.height(16.dp)) TextField(value = "", onValueChange = {}, label = { Text("密码") }, visualTransformation = PasswordVisualTransformation()) Spacer(modifier = Modifier.height(16.dp)) Button(onClick = {}) { Text("登录") } } }
方案二:使用animateFloatAsState+状态监听
如果偏好使用Compose的animate*AsState系列API,可以通过状态枚举+动画完成回调来实现顺序控制:
enum class AnimationStage { LOGO_ENTRY, LOGO_EXIT, LOGIN_ENTRY } @Composable fun SplashLoginTransition() { var currentStage by remember { mutableStateOf(AnimationStage.LOGO_ENTRY) } // Logo缩放动画 val logoScale by animateFloatAsState( targetValue = when (currentStage) { AnimationStage.LOGO_ENTRY -> 1f AnimationStage.LOGO_EXIT -> 0f else -> 0f }, animationSpec = tween(1000), // 监听Logo退场动画完成,切换到登录页入场阶段 finishedListener = { if (currentStage == AnimationStage.LOGO_EXIT) { currentStage = AnimationStage.LOGIN_ENTRY } } ) // 登录页缩放动画 val loginScale by animateFloatAsState( targetValue = if (currentStage == AnimationStage.LOGIN_ENTRY) 1f else 0f, animationSpec = tween(1000) ) // 初始触发:Logo入场完成后切换到退场阶段 LaunchedEffect(Unit) { delay(1000) // 可选停留时间 currentStage = AnimationStage.LOGO_EXIT } Box(modifier = Modifier.fillMaxSize()) { if (currentStage != AnimationStage.LOGIN_ENTRY) { Image( painter = painterResource(id = R.drawable.your_app_logo), contentDescription = "App Logo", modifier = Modifier .align(Alignment.Center) .scale(logoScale) ) } if (currentStage == AnimationStage.LOGIN_ENTRY) { LoginScreen(modifier = Modifier.fillMaxSize().scale(loginScale)) } } }
为什么Crossfade/AnimatedContent不适合?
Crossfade和AnimatedContent默认是交叉并行的动画逻辑,即旧内容退场和新内容入场同时进行。如果要强行改成顺序执行,需要自定义复杂的动画Spec,反而不如上面两种方案直观可控,尤其是需要精确判断前一个动画完成时机的场景。
注意事项
- 动画时长、缓动效果可以通过替换
tween为spring、keyframes等AnimationSpec自定义 - 如果需要同时添加透明度动画,可以额外创建
Animatable或animateFloatAsState控制alpha值 - 所有
Animatable和状态变量都要用remember包裹,避免Compose重组时重置状态
内容的提问来源于stack exchange,提问作者zzz
相关产品推荐
相关产品推荐

