Compose 动画,让页面动起来

概述

Compose 动画 可以分为两类: 高级别的 API 和 低级别的 API,高级别的API开箱即用,低级别的API 可以基于协程完成任何状态驱动的动画效果,高级别的API底层都是由低级别API支持的

Compose 动画API 分类

高级别动画API

AnimatedVisibility

  1. AnimatedVisibility是一个容器类的Composable,需要接收一个Boolean型的visible参数控制content是否可见,content在出现与消失时,会伴随着过渡动画效果。
  2. Compose额外提供了RowScope.AnimatedVisibility和ColumnScope. AnimatedVisibility两个扩展方法,我们可以在Row或者Column中调用AnimatedVisibility
// 当editable为true/false时,Text将会淡入/淡出屏幕
var editable by remember { mutableStateOf(true) }
AnimatedVisibility(visible = editable) {
    Text(text = "Edit")
}

也可以通过设置 EnterTransition和ExitTransition来定制出场与离场过渡动画。

MutableTransitionState

AnimatedVisibility还有一个接收MutableTransitionState类型参数的重载方法,用于 监听动画状态

Modifier.animateEnterExit

在AnimatedVisibility的content中,可以使用Modifier.animateEnterExit为每个子元素单独设置进出屏幕的过渡动画。

自定义Enter/Exit动画

如果想在EnterTransition和ExitTransition之外再增加其他动画效果,可以在AnimatedVisibilityScope内设置transition。添加到transition的动画都会在AnimatedVisibility进出屏幕动画的同时运行。AnimatedVisibility会等到Transition中的所有动画都完成后,再移出屏幕。

AnimatedContent

AnimatedContent和AnimatedVisibility相类似,区别在于AnimatedVisibility用来添加组件的出场与离场过渡动画,而AnimatedContent则是用来实现不同组件间的切换动画。

ContentTransform自定义动画

AnimatedContent可以将transitionSpec参数指定为一个ContentTransform来自定义动画效果。ContentTransform也是由EnterTransition与ExitTransition组合的,可以使用togetherWith中缀运算符将EnterTransition与ExitTransition组合起来。

SizeTranstion定义大小动画

在使用ContentTransform来创建自定义过渡动画的同时,还可以使用using操作符连接SizeTransform。实现尺寸变化的过渡动画。

定义子元素动画

与AnimatedVisibility一样,AnimatedContent内部的子组件也可以通过Modifier.animatedEnterExit单独指定动画。

自定义Enter/Exit动画

通过AnimatedContent的定义可知,其content同样是在AnimatedVisibilityScope作用域中,所以内部也可以通过transition添加额外的自定义动画。

Crossfade

如果只需要淡入淡出效果,可以使用Crossfade替代AnimatedContent。更正确的说法应该是AnimatedContent是Crossfade的一种泛化。

Crossfade无法实现SizeTransform那样尺寸变化的动画效果,如果content变化前后尺寸不同,想使用动画进行过渡,可以使用:

  1. AnimatedContent+SizeTranform
  2. Crossfade + Modifier.animateContentSize

Modifier.animateContentSize

animateContentSize是一个Modifier修饰符方法。它的用途非常专一,当容器尺寸发生变化时,会通过动画进行过渡。

低级别动画API

animate*AsState

  1. animated*AsState是最常用的低级别API之一,它类似于传统视图中的属性动画,可以自动完成从当前值到目标值过渡的估值计算。

  2. Compose为常用的数据类型都提供了animate*AsState方法,例如Float、Color、Dp、Size、Bounds、Offset、Rect、Int、IntOffset和InSize等

Animatable

Animatble是一个数值包装器,它的animateTo方法可以根据数值的变化设置动画效果,animate*AsState背后就是基于Animatable实现的。

val buttonColor = remember { Animatable(Color.Gray) }  
LaunchedEffect(flag) {  
            //animateTo  
            buttonColor.animateTo(  
                targetValue = if (isLike) Color.Red else Color.Gray,  
                animationSpec = tween(3000)  
            )  
}  

Animatable中包括animateTo在内的许多API都是挂起函数,需要在CoroutineScope中执行,可以使用LaunchedEffect为其提供所需的环境。

Transition过渡动画

AnimateState以及Animatable都是针对单个目标值的动画,而Transition可以面向多个目标值应用动画并保持它们同步结束。Transition的作用更像是传统视图体系动画中的AnimationSet。

updateTransition

使用updateTransition创建一个Transition动画,使用animate*来声明每个动画属性其在不同状态时的数值信息,当Transition所依赖的状态发生改变时,其中每个属性状态都会得到相应的更新。

val transition = updateTransition(targetState = selectedState, label = "switch_transition")
val textAlpha by transition.animateFloat(  
            transitionSpec = {  
                tween(1000)  
            }, label = ""  
        ) {  
            when (it) {  
                SwitchState.CLOSE -> 1f  
                SwitchState.OPEN -> 0f  
            }  
        }
createChildTransition创建子动画

Transition可以使用createChildTransition创建子动画,各自只需要关心自己的状态,能够更好地实现关注点分离。

与AnimatedVisibility和AnimatedContent配合使用

AnimatedVisibility和AnimatedContent有针对Transition的扩展函数,将Transition的State转换成所需的TargetState。

封装并复用Transition动画

如果希望把Transition动画的实现与用户界面分开,可以通过创建一个持有所有动画值的类和一个返回该类实例的“更新”函数来做到这一点。Transition动画的实现被提取到单独的函数中,便于后续进行复用。

rememberInfiniteTransition

  1. InfinitTransition从名字上便可以知道其就是一个无限循环版的Transition。一旦动画开始执行,便会不断循环下去,直至Composable生命周期结束。

  2. 子动画可以用animateColor、animatedFloat或animatedValue等进行添加,另外还需要指定infiniteRepeatableSpec来设置动画循环播放方式。

AnimationSpec动画规格

Compose提供了多种AnimationSpec的子类,分别基于不同的VectorizedAnimationSpec实现不同动画效果的计算。例如TweenSpec用来实现两点间的补间动画,SpringSpec实现基于物理效果的动画,SnapSpec是一个即时生效的动画。

spring弹跳动画

@Stable
fun <T> spring(
    dampingRatio: Float = Spring.DampingRatioNoBouncy,
    stiffness: Float = Spring.StiffnessMedium,
    visibilityThreshold: T? = null
): SpringSpec<T> =
    SpringSpec(dampingRatio, stiffness, visibilityThreshold)
  1. dampingRation: dampingRation表示弹簧的阻尼比。
  2. stiffness: stiffness定义弹簧的刚度值。
  3. visibilityThreshold: 参数visibilityThreshold是一个泛型,此泛型与targetValue类型保持一致。由开发者指定一个阈值,当动画到达这个阈值时,动画会立即停止。

tween补间动画

使用tween可以创建一个TweenSpec实例,TweenSpec是DurationBasedAnimationSpec的子类。从基类名字可以感受到,TweenSpec的动画必须在规定时间内完成。

  val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = tween(
        durationMillis = 300,//动画执行时间(ms)
        delayMillis = 50,//可以指定动画的延迟执行
        easing = LinearOutSlowInEasing//衰减曲线动画效果
    ), label = ""
)

keyframes关键帧动画

相对于tween动画只能在开始和结束两点之间应用动画效果,keyframes可以更精细地控制动画,它允许在开始和结束之间插入关键帧节点,节点与节点之间的动画过渡可以应用不同效果。

repeatable循环动画

使用repeatable可以创建一个RepeatableSpec实例。前面所介绍的动画都是单次动画,而这里的repeatable是一个可循环播放的动画,可以指定TweenSpec或者KeyFramesSpec以及循环播放的方式。

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = repeatable(
        iterations = 3,
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    ), label = ""
)

infiniteRepeatable无限循环动画

infiniteRepeatable顾名思义,就是无限执行的RepeatableSpec,因此没有iterations参数。它将创建并返回一个InfiniteRepeatableSpec实例。

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = infiniteRepeatable(
        animation = tween(durationMillis = 300),
        repeatMode = RepeatMode.Reverse
    ),
    label = ""
)

snap快闪动画

snap会创建一个SnapSpec实例,这是一种特殊动画,它的targetValue发生变化时,当前值会立即更新为targetValue。由于没有中间过渡,动画会瞬间完成,常用于跳过过场动画的场景。我们也可以设置delayMillis参数来延迟动画的启动时间。

val value by animateFloatAsState(
    targetValue = 1f,
    animationSpec = snap(delayMillis = 50), label = ""
)

使用Easing控制动画节奏

  1. Easing本质上就是一个基于时间参数的函数(实际是一个单方法接口),它的输入和输出都是0f~1f的浮点数值。

  2. 输入值表示当前动画在时间上的进度,返回值是则是当前value的进度,1.0表示已经达到targetValue。不同的Easing算法可以实现不同的动画加速、减速效果,因此也可以将Easing理解为动画的瞬时速度。

AnimationVector动画矢量值

矢量动画是基于动画矢量值AnimationVector计算的。前面的章节中我们了解到,animae*AsState基于Animatable将Color、Float、Dp等数据类型的数值转换成可动画类型,其本质就是将这些数据类型转换成AnimationVector参与动画计算。

@Suppress("NotCloseable")  
class Animatable<T, V : AnimationVector>(  
    initialValue: T,     //T类型的动画初始值
    val typeConverter: TwoWayConverter<T, V>,   //将T类型的数值与V类型的AnimationVector进行转换
    private val visibilityThreshold: T? = null,   //动画消失的阈值,默认为null
    val label: String = "Animatable"  
) {..}

TwoWayConverter

interface TwoWayConverter<T, V : AnimationVector> {  
    val convertToVector: (T) -> V   
    val convertFromVector: (V) -> T  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值