欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

JetpackCompose实现动画效果的方法详解,

来源: 开发者 投稿于  被查看 13408 次 评论:15

JetpackCompose实现动画效果的方法详解,


目录
  • 概述
  • 低级别动画API
    • animate*AsState
    • 使用Animatable实现颜色变化效果
    • 使用updateTransition实现颜色和圆角动画
    • rememberInfiniteTransition
    • TargetBasedAnimation
  • 自定义动画
    • AnimationSpec
    • Easing
    • AnimationVector
  • 高级动画

    概述

    compose 为支持动画提供了大量的 api,通过这些 api 我们可以轻松实现动画效果

    ps:这些 api 的原理与 Flutter 很接近,与原生的 api 相去甚远

    你可以提前看看用 compose 实现的一个放大缩小动画,总的来说还是比较流畅:

    低级别动画 API

    animate*AsState

    所能处理属性的种类:Float、Color、Dp、Size、Bounds、Offset、Rect、Int、IntOffset 和 IntSize

    通过 animate*AsState 我们可以实现单一属性的动画效果,我们只需要提供目标值就可以自动从当前进度动画过渡到目标值

    实现放大动画

    1.代码

    @Composable
    fun animSize() {
        val enable = remember {
            mutableStateOf(true)
        }
        val size =
            animateSizeAsState(targetValue = if (enable.value) Size(50f, 50f) else Size(300f, 300f))
        Column(
            modifier = Modifier.fillMaxSize(1f),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Image(
                modifier = Modifier
                    .size(size.value.width.dp, size.value.height.dp)
                    .clickable {
                        enable.value = !enable.value
                    },
                painter = painterResource(id = R.drawable.apple),
                contentDescription = ""
            )
        }
    }

    2.实现效果

    实现颜色变化动画

    1.代码

    @Composable
    fun animColor() {
        val enable = remember {
            mutableStateOf(true)
        }
        val colors = animateColorAsState(targetValue = if (enable.value) Color.Green else Color.Red)
        val size = animateIntSizeAsState(
            targetValue = if (enable.value) IntSize(100, 100) else IntSize(
                300,
                300
            )
        )
        Column(
            modifier = Modifier.fillMaxWidth(1f),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Box(
                modifier = Modifier
                    .size(size.value.width.dp, size.value.height.dp)
                    .height(400.dp)
                    .background(
                        color = colors.value,
                        shape = if (enable.value) RectangleShape else CircleShape
                    )
            ) {
    
            }
        }
    }

    2.效果

    使用 Animatable 实现颜色变化效果

    Animatable 是一个值容器,我们可以通过调用 animateTo 实现动画效果。动画执行过程中如果再次开启动画会中断当前动画。

    Animatable 动画执行过程中值的变化是在协程中执行的,所以 animateTo 是一个挂起操作

    1.代码

    @Composable
    fun animChangeColor() {
        val color = remember {
            Animatable(Color.Red)
        }
        val state = remember {
            mutableStateOf(true)
        }
        LaunchedEffect(state.value) {
            color.animateTo(if (state.value) Color.Red else Color.Magenta)
        }
        Box(Modifier.fillMaxSize(1f), contentAlignment = Alignment.Center) {
            Box(
                modifier = Modifier
                    .background(color.value, shape = RoundedCornerShape(30.dp))
                    .size(200.dp)
                    .clickable {
                        state.value = !state.value
                    }, contentAlignment = Alignment.Center
            ) {
                Text(
                    text = "颜色动画",
                    style = TextStyle(color = Color.White, fontSize = 40.sp)
                )
            }
        }
    }

    2.效果

    使用 updateTransition 实现颜色和圆角动画

    使用 updateTransition 可以实现多个动画组合的效果。

    例如:我们可以在动画执行过程中同时执行大小和颜色变化效果

    本例中我们定义了一个枚举用来控制动画,枚举可以定义多个,分别用来对应动画的多个状态

    1.代码

    @Composable
    fun animupdateTransition() {
        var state by remember {
            mutableStateOf(BoxState.Collapsed)
        }
        val transition = updateTransition(targetState = state, label = "")
    
        val round = transition.animateDp(label = "") {
            when (it) {
                BoxState.Collapsed -> 40.dp
                BoxState.Expanded -> 100.dp
            }
        }
        val color = transition.animateColor(label = "") {
            when (it) {
                BoxState.Collapsed -> Color.Red
                BoxState.Expanded -> Color.Green
            }
        }
        Box(Modifier.fillMaxSize(1f),contentAlignment = Alignment.Center) {
            Box(
                modifier = Modifier
                    .size(300.dp)
                    .background(
                        color.value,
                        shape = RoundedCornerShape(corner = CornerSize(round.value))
                    )
                    .clickable {
                        state =
                            if (state == BoxState.Collapsed) BoxState.Expanded else BoxState.Collapsed
                    },contentAlignment = Alignment.Center
            ) {
                Text(text = "点击开始动画",style = TextStyle(color = Color.White,fontSize = 20.sp))
            }
        }
    }
    private enum class BoxState {
        Collapsed,
        Expanded
    }

    2.效果

    rememberInfiniteTransition

    rememberInfiniteTransition 的使用和 updateTransition 基本一样,不同的是 rememberInfiniteTransition 的动画一旦开始便会一直反复运行下去,只有被移除动画才能结束

    1.代码

    @Composable
    fun rememberInfiniteTransition1() {
        val infiniteTransition = rememberInfiniteTransition()
        val color by infiniteTransition.animateColor(
            initialValue = Color.Red,
            targetValue = Color.Green,
            animationSpec = infiniteRepeatable(
                animation = tween(1000, easing = LinearEasing),
                repeatMode = RepeatMode.Reverse
            )
        )
    
        Box(Modifier.fillMaxSize(1f), contentAlignment = Alignment.Center) {
            Box(
                Modifier
                    .fillMaxSize(0.8f)
                    .background(color),
                contentAlignment = Alignment.Center
            ) {
                Text(
                    text = "公众号:安安安安卓 原创,禁抄袭",
                    style = TextStyle(color = Color.White, fontSize = 30.sp)
                )
            }
        }
    }

    2.效果 ​​​​​​​

    TargetBasedAnimation

    TargetBasedAnimation 可以控制动画的执行时间,还可以延迟一段时间再开启动画。

    1.代码

    @Composable
    fun animTargetBasedAnimation() {
        var state by remember {
            mutableStateOf(0)
        }
        val anim = remember {
            TargetBasedAnimation(
                animationSpec = tween(2000),
                typeConverter = Float.VectorConverter,
                initialValue = 100f,
                targetValue = 300f
            )
        }
        var playTime by remember { mutableStateOf(0L) }
        var animationValue by remember {
            mutableStateOf(0)
        }
    
        LaunchedEffect(state) {
            val startTime = withFrameNanos { it }
            println("进入协程:")
            do {
                playTime = withFrameNanos { it } - startTime
                animationValue = anim.getValueFromNanos(playTime).toInt()
            } while (!anim.isFinishedFromNanos(playTime))
    
        }
        Box(modifier = Modifier.fillMaxSize(1f),contentAlignment = Alignment.Center) {
            Box(modifier = Modifier
                .size(animationValue.dp)
                .background(Color.Red,shape = RoundedCornerShape(animationValue/5))
                .clickable {
                    state++
                },contentAlignment = Alignment.Center) {
                Text(text = animationValue.toString(),style = TextStyle(color = Color.White,fontSize = (animationValue/5).sp))
            }
        }
    }

    2.效果 ​​​​​​​

    自定义动画

    AnimationSpec

    AnimationSpec 可以自定义动画的行为,效果类似于原生动画中的估值器。

    SpringSpec 弹簧效果

    1.代码

    @Composable
    fun animSpring() {
        val state = remember {
            mutableStateOf(true)
        }
        var value = animateIntAsState(
            targetValue = if (state.value) 300 else 100,
            animationSpec = spring(
                dampingRatio = Spring.DampingRatioHighBouncy,
                stiffness = Spring.StiffnessVeryLow
            )
        )
    
        Box(
            Modifier
                .fillMaxSize(1f)
                .padding(start = 30.dp), contentAlignment = Alignment.CenterStart
        ) {
            Box(
                Modifier
                    .width(value.value.dp)
                    .height(80.dp)
                    .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp))
                    .clickable {
                        state.value = !state.value
                    }, contentAlignment = Alignment.CenterStart
            ) {
                Text(text = "哈哈哈", style = TextStyle(color = Color.White, fontSize = 20.sp))
            }
        }
    }

    2.效果 ​​​​​​​

    TweenSpec 动画时间可控

    1.代码

    @Composable
    fun animTweenSpec() {
        val state = remember {
            mutableStateOf(true)
        }
        val value = animateIntAsState(
            targetValue = if (state.value) 300 else 100,
            animationSpec = tween(
                durationMillis = 1500,
                delayMillis = 200,
                easing = LinearEasing
            )
        )
    
        Box(
            Modifier
                .fillMaxSize(1f)
                .padding(start = 50.dp), contentAlignment = Alignment.CenterStart
        ) {
            Box(
                Modifier
                    .width(value.value.dp)
                    .height(100.dp)
                    .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp))
                    .clickable {
                        state.value = !state.value
                    }
            ) {
    
            }
        }
    
    }

    2.效果 ​​​​​​​

    FrameSpec

    1.代码

    @Composable
    fun animkeyframesSpec() {
        var state by remember {
            mutableStateOf(true)
        }
        val value by animateIntAsState(
            targetValue = if (state) 300 else 100,
            animationSpec = keyframes {
                durationMillis = 2000
                0 at 700 with LinearOutSlowInEasing
                700 at 1400 with FastOutLinearInEasing
                1400 at 2000
            })
    
        Box(Modifier.fillMaxSize(1f), contentAlignment = Alignment.CenterStart) {
            Box(
                Modifier
                    .width(value.dp)
                    .height(100.dp)
                    .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp))
                    .clickable {
                        state = !state
                    }
            ) {
    
            }
        }
    }

    2.效果

    RepeatableSpec 实现有限次数的重复动画

    执行有限次数动画后自动停止

    1.代码

    @Composable
    fun animrepeatableSpec() {
        var state by remember {
            mutableStateOf(true)
        }
        val value by animateIntAsState(
            targetValue = if (state) 300 else 100,
            animationSpec = repeatable(
                iterations = 5,//动画重复执行的次数,设置多少就执行多少次
                animation = tween(durationMillis = 1000),
                repeatMode = RepeatMode.Reverse
            )
        )
        Box(
            Modifier
                .fillMaxSize(1f)
                .padding(start = 30.dp), contentAlignment = Alignment.CenterStart) {
            Box(
                Modifier
                    .width(value.dp)
                    .height(100.dp)
                    .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp))
                    .clickable {
                        state = !state
                    }
            ) {
    
            }
        }
    }

    2.效果

    代码中设置了重复 5 次,所以反复执行五次后动画结束

    InfiniteRepeatableSpec 无限次数执行动画

    动画会无限次的执行下去,直到视图被移除

    1.代码

    @Composable
    fun animinfiniteRepeatableSpec() {
        var state by remember {
            mutableStateOf(true)
        }
        val value by animateIntAsState(
            targetValue = if (state) 300 else 100,
            animationSpec = infiniteRepeatable(
                animation = tween(durationMillis = 1000),
                repeatMode = RepeatMode.Reverse
            )
        )
        Box(
            Modifier
                .fillMaxSize(1f)
                .padding(start = 30.dp), contentAlignment = Alignment.CenterStart) {
            Box(
                Modifier
                    .width(value.dp)
                    .height(100.dp)
                    .background(Color.Red, RoundedCornerShape(topEnd = 30.dp, bottomEnd = 30.dp))
                    .clickable {
                        state = !state
                    }
            ) {
                Text(text = "公众号:安安安安卓 原创,禁转载")
            }
        }
    }

    2.效果

    Easing

    Easing 类似于我们原生动画中的差值器

    有以下几种选择:

    • FastOutSlowInEasing
    • LinearOutSlowInEasing
    • FastOutLinearInEasing
    • LinearEasing
    • CubicBezierEasing

    这几种实现的效果和 android 原生实现的动画差值器差距很大,甚至看不出有啥效果,所以代码我就不放了。有清楚原因的读者可以联系我

    实现效果:

    AnimationVector

    大多数 Compose 动画 API 都支持将 Float、Color、Dp 以及其他基本数据类型作为开箱即用的动画值,但有时我们需要为其他数据类型(包括我们的自定义类型)添加动画效果

    本例中实现颜色和大小的变换动画

    代码中我们定义了一个 AnimSize 类,类中的第一个参数是颜色数据,第二个参数是尺寸数据。动画执行过程中会同事改变颜色和控件尺寸效果。

    1.代码

    @Composable
    fun animAnimationVector() {
        var state by remember {
            mutableStateOf(true)
        }
        val value by animateValueAsState(
            targetValue = if (state) AnimSize(0xffff5500, 100f) else AnimSize(0xff00ff00, 300f),
            typeConverter = TwoWayConverter(
                convertToVector = {
    //                AnimationVector2D(target.color.toFloat(), target.size)
                    AnimationVector2D(it.color.toFloat(), it.size)
                },
                convertFromVector = {
                    AnimSize(it.v1.toLong(), it.v2)
                }
            )
        )
        println("颜色:${value.color}")
        Box(modifier = Modifier.fillMaxSize(1f).padding(30.dp), contentAlignment = Alignment.Center) {
            Box(
                modifier = Modifier
                    .size(value.size.dp)
    //                .size(300.dp)
                    .background(Color(value.color), RoundedCornerShape(30.dp))
                    .clickable {
                        state = !state
                    }
            ) {
    
            }
        }
    }
    
    data class AnimSize(val color: Long, val size: Float)

    2.效果

    缺点是执行颜色变化过程中有闪烁

    高级动画

    高级动画一般指封装性较高的动画,使用较为简单,主要有以下三种:

    因高级动画效果不明显,gif 很难展现出效果,所以这里不放代码和效果图了

    1. AnimatedVisibility
    2. animateContentSize
    3. Crossfade

    以上就是Jetpack Compose实现动画效果的方法详解的详细内容,更多关于Jetpack Compose动画的资料请关注3672js教程其它相关文章!

    您可能感兴趣的文章:
    • 通过Jetpack Compose实现双击点赞动画效果
    • 利用Jetpack Compose绘制可爱的天气动画
    • Android Jetpack Compose实现列表吸顶效果
    • 利用Jetpack Compose实现主题切换功能
    • Android Jetpack Compose无限加载列表

    用户评论