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 避免过度使用,在复杂列表或高频触发动画的场景,过多动画可能引发性能问题。

相关推荐
q***48411 小时前
Redis Desktop Manager(Redis可视化工具)安装及使用详细教程
android·前端·后端
renxhui1 小时前
Dart 速通攻略(面向 Android 工程师)
android·flutter·dart
摇滚侠1 小时前
零基础小白自学Git_Github教程,Git 与 GitHub 的历史起源,笔记05
笔记·git·github
Frank_HarmonyOS1 小时前
MVI模式
android
m***9821 小时前
Redis6.2.6下载和安装
android·前端·后端
黑客思维者2 小时前
LLM底层原理学习笔记:上下文窗口扩展技术如何突破记忆瓶颈,解锁大模型的长文本理解力
笔记·学习·llm·长文本·上下文窗口
未来之窗软件服务2 小时前
幽冥大陆(三十九)php二维数组去重——东方仙盟筑基期
android·开发语言·算法·php·仙盟创梦ide·东方仙盟·东方仙盟sdk
摇滚侠2 小时前
零基础小白自学 Git_Github 教程,Git 分支概念,笔记07
笔记·git·github
摇滚侠2 小时前
零基础小白自学 Git_Github 教程,仓库的其它功能,笔记09
笔记·git·github