Compose笔记(五十八)--LinearOutSlowInEasing

这一节主要了解一下LinearOutSlowInEasing,在Jetpack Compose中,LinearOutSlowInEasing是一个预定义的缓动函数,用于控制动画的速度变化曲线,使动画看起来更自然、流畅以符合特定交互预期,简单总结如下:

API

Kotlin 复制代码
val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f)

本质是三次贝塞尔曲线实现的缓动函数,参数(0f,0f,0.2f,1f)定义了速率变化轨迹:

动画前半段:速率接近匀速(Linear Out);

动画后半段:速率逐渐降低(Slow In),结束时平滑停止。

场景:

1 抽屉式导航栏、底部弹窗、列表项展开/折叠。

2 按钮点击反馈、图片浏览器的缩略图展开、卡片选中效果。

3 按钮状态切换(启用/禁用)、页面背景渐变、通知栏淡入淡出。

栗子:

Kotlin 复制代码
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.LinearOutSlowInEasing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.size
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch

@Composable
fun ProgressBarDemo() {
    val progress = remember { Animatable(0f) }
    val coroutineScope = rememberCoroutineScope()

    
    LaunchedEffect(Unit) {
        coroutineScope.launch {
            progress.animateTo(
                targetValue = 1f,
                animationSpec = tween(
                    durationMillis = 1500,
                    easing = LinearOutSlowInEasing
                )
            )
        }
    }

    Canvas(modifier = Modifier.size(100.dp)) {
        
        drawCircle(
            color = Color.LightGray,
            radius = size.minDimension / 2 - 4.dp.toPx(),
            style = Stroke(width = 8.dp.toPx())
        )
       
        drawArc(
            color = Color.Blue,
            startAngle = -90f,
            sweepAngle = progress.value * 360f,
            useCenter = false,
            style = Stroke(width = 8.dp.toPx(), cap = StrokeCap.Round)
        )
    }
}
Kotlin 复制代码
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Menu
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.scale
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DrawerSidebarDemo() {
    var isDrawerOpen by remember { mutableStateOf(false) }

    
    val drawerOffset by animateDpAsState(
        targetValue = if (isDrawerOpen) 0.dp else -280.dp,
        animationSpec = tween(
            durationMillis = 400,
            easing = LinearOutSlowInEasing
        )
    )

    
    val contentScale by animateFloatAsState(
        targetValue = if (isDrawerOpen) 0.95f else 1f,
        animationSpec = tween(
            durationMillis = 400,
            easing = LinearOutSlowInEasing
        )
    )
    val contentAlpha by animateFloatAsState(
        targetValue = if (isDrawerOpen) 0.7f else 1f,
        animationSpec = tween(
            durationMillis = 400,
            easing = LinearOutSlowInEasing
        )
    )
    val contentShadow by animateDpAsState(
        targetValue = if (isDrawerOpen) 10.dp else 0.dp,
        animationSpec = tween(
            durationMillis = 400,
            easing = LinearOutSlowInEasing
        )
    )

    Box(modifier = Modifier.fillMaxSize()) {
        
        Column(
            modifier = Modifier
                .width(280.dp)
                .fillMaxHeight()
                .offset(x = drawerOffset)
                .background(MaterialTheme.colorScheme.primaryContainer)
                .padding(20.dp)
        ) {
            Text("侧边菜单", style = MaterialTheme.typography.headlineSmall)
            Spacer(modifier = Modifier.height(30.dp))
            listOf("首页", "消息", "设置", "关于").forEach { item ->
                Text(
                    text = item,
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(12.dp),
                    style = MaterialTheme.typography.bodyLarge
                )
            }
        }

        
        Column(
            modifier = Modifier
                .fillMaxSize()
                .scale(contentScale)
                .alpha(contentAlpha)
                .shadow(contentShadow)
                .background(MaterialTheme.colorScheme.surface)
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            TopAppBar(
                title = { Text("主页面") },
                navigationIcon = {
                    IconButton(onClick = { isDrawerOpen = !isDrawerOpen }) {
                        Icon(Icons.Outlined.Menu, contentDescription = "菜单")
                    }
                }
            )
            Spacer(modifier = Modifier.height(50.dp))
            Text(
                text = if (isDrawerOpen) "抽屉已打开" else "点击左上角菜单打开抽屉",
                style = MaterialTheme.typography.bodyLarge
            )
        }
    }
}

注意:

1 避免与过短的动画时长搭配,LinearOutSlowInEasing的"慢入"阶段需要一定时间才能体现缓冲效果。

2 避免过度使用,在复杂列表或高频触发动画的场景,过多动画可能引发性能问题。

相关推荐
前进的李工2 小时前
Agent 学习笔记 05:系统提示词应该怎么写
笔记·学习·prompt·agent·个人开发·few-shot·ai应用开发
三少爷的鞋3 小时前
Android 架构七条铁律:架构师真正设计的不是代码,而是秩序
android
蒸蒸yyyyzwd4 小时前
后端学习笔记三个知识
笔记·八股
xiangxiongfly9155 小时前
Android GsonFactory总结
android·gson·gsonfactory
Zhao-Jichao7 小时前
AI Prompting for Everyone 课程笔记 - P19 数据分析 Data Analysis
人工智能·笔记·数据分析
WythePlus7 小时前
Android 14 SELinux策略深度解析
android·安全策略·selinux策略
旖旎夜光7 小时前
【C++ 基础入门】类和对象(上):封装、对象模型与 this 指针
开发语言·c++·笔记·学习
ACP广源盛139246256737 小时前
选型笔记:Type‑C 扩展坞方案评估|DP1.2 转 HDMI2.0 桥接芯片 GSV2201D@ACP
笔记·硬件架构·硬件工程·视频·国产芯片
李长渊哦9 小时前
Qoder 自定义子智能体(Subagent)学习笔记
chrome·笔记·学习
pixelpilot9 小时前
Deta Surf:把笔记、文件和网页塞进同一个 AI 工作台
笔记·其他