compose动画从底层基础到顶层高级应用(二)核心API之--AnimateAsState

154 阅读5分钟

状态动画AnimateAsState

上一篇文章中,学习了动画最基本的AnimationSpec,今天来往上层走,应用这些动画规格。 AnimateAsState是使用最简单的动画,根据一个状态来触发动画,消费即忘,下面一个个来看如何使用和效果展示。

animateFloatAsState

定义:
@Composable
public fun animateFloatAsState(
    targetValue: Float,
    animationSpec: AnimationSpec<Float> = defaultAnimation,
    visibilityThreshold: Float = 0.01f,
    label: String = "FloatAnimation",
    finishedListener: ((Float) -> Unit)? = null
): State<Float>
参数解释:
  • targetValue:目标值,这里是Float类型的目标值。
  • animationSpec:动画规格,也就是上一篇文章讲到的动画基础,参考compose动画从底层基础到顶层高级应用(一)动画规格--AnimationSpec
  • visibilityThreshold:动画提前结束的阈值,在上一篇中也详细解释过了。
  • label:给这个动画打一个标签,调试用。
  • finishedListener:动画完成的回调,完成时回调targetValue的值。
使用:

浮点数类型的值,我想到的一般是颜色的alpha值,那么下面就写一个组件颜色透明度变化的动画。

@Preview
@Composable
fun AnimationExample() {
	// 使用enable这个状态去控制动画的开始
    var enable by remember { mutableStateOf(false) }
    // 使用isRunning去记录动画是否运行
    var isRunning by remember { mutableStateOf(false) }
    
	// Float类型状态动画,目标值根据enable这个状态去设置对应的值,
	// 动画规格使用的是tween动画,持续2秒,
	// finishedListener回调触发时更新isRunning状态
    val alpha by animateFloatAsState(
        targetValue = if (enable) 1f else 0f,
        animationSpec = tween(durationMillis = 2000),
        finishedListener = {
            isRunning = false
        }
    )

    Box(
        modifier = Modifier
            .fillMaxSize()
            .padding(10.dp),
        contentAlignment = Alignment.Center
    ) {
        Box(
            modifier = Modifier
                .padding(top = 150.dp)
                .size(100.dp)
                .alpha(alpha) //直接在modifier的alpha函数中传入动画状态
                .background(Color(0xFF4CAF50))
                .align(Alignment.TopCenter)
        )
        Button(
            onClick = {
                isRunning = true
                enable = !enable //点击按钮时触发enable,继而触发动画
            },
            modifier = Modifier
                .size(width = 120.dp, height = 40.dp)
                .align(Alignment.Center),
        ) {
            Text(
                text = if (isRunning) "Running" else "Ready",
                color = Color.White
            )
        }
    }
}
效果:

在这里插入图片描述

接下来的几个不同类型的状态动画我就不一一展示代码,不然太长了,用法都是一样的,有特殊的会详细解释。

animateDpAsState

Dp类型的动画,一般用于高度或者宽度等场景。

定义:
@Composable
public fun animateDpAsState(
    targetValue: Dp,
    animationSpec: AnimationSpec<Dp> = dpDefaultSpring,
    label: String = "DpAnimation",
    finishedListener: ((Dp) -> Unit)? = null
): State<Dp>

animateSizeAsState

Size类型动画,Size(width, height)

定义:
@Composable
public fun animateSizeAsState(
    targetValue: Size,
    animationSpec: AnimationSpec<Size> = sizeDefaultSpring,
    label: String = "SizeAnimation",
    finishedListener: ((Size) -> Unit)? = null
): State<Size>

animateOffsetAsState

偏移量类型的动画,Offset(x, y)

定义:
@Composable
public fun animateOffsetAsState(
    targetValue: Offset,
    animationSpec: AnimationSpec<Offset> = offsetDefaultSpring,
    label: String = "OffsetAnimation",
    finishedListener: ((Offset) -> Unit)? = null
): State<Offset>

animateRectAsState

矩形类型的动画,Rect封装了Offset,Size等类型。

定义:
@Composable
public fun animateRectAsState(
    targetValue: Rect,
    animationSpec: AnimationSpec<Rect> = rectDefaultSpring,
    label: String = "RectAnimation",
    finishedListener: ((Rect) -> Unit)? = null
): State<Rect>

animateIntAsState

Int类型的动画

定义:
@Composable
public fun animateIntAsState(
    targetValue: Int,
    animationSpec: AnimationSpec<Int> = intDefaultSpring,
    label: String = "IntAnimation",
    finishedListener: ((Int) -> Unit)? = null
): State<Int>

animateIntOffsetAsState

IntOffset类型的动画

定义:
@Composable
public fun animateIntOffsetAsState(
    targetValue: IntOffset,
    animationSpec: AnimationSpec<IntOffset> = intOffsetDefaultSpring,
    label: String = "IntOffsetAnimation",
    finishedListener: ((IntOffset) -> Unit)? = null
): State<IntOffset>

animateIntSizeAsState

IntSize类型的动画

定义:
@Composable
public fun animateIntSizeAsState(
    targetValue: IntSize,
    animationSpec: AnimationSpec<IntSize> = intSizeDefaultSpring,
    label: String = "IntSizeAnimation",
    finishedListener: ((IntSize) -> Unit)? = null
): State<IntSize>

animateColorAsState

颜色类型动画。

定义:
@Composable
public fun animateColorAsState(
    targetValue: Color,
    animationSpec: AnimationSpec<Color> = colorDefaultSpring,
    label: String = "ColorAnimation",
    finishedListener: ((Color) -> Unit)? = null
): State<Color>

animateValueAsState

自定义类型动画,前面都是系统提供的封装好的类型,那么如果遇到自定义的类型,就需要使用到这个函数了。

定义:
@Composable
public fun <T, V : AnimationVector> animateValueAsState(
    targetValue: T,
    typeConverter: TwoWayConverter<T, V>,
    animationSpec: AnimationSpec<T> = remember { spring() },
    visibilityThreshold: T? = null,
    label: String = "ValueAnimation",
    finishedListener: ((T) -> Unit)? = null
): State<T>
参数:
  • typeConverter:这是跟前面不一样的参数,需要传递的是将自定义类型和AnimationVector之间进行转换的逻辑。
AnimationVector:

compose动画会将不同的类型向量化,才能通过数学模型进行动画处理,AnimationVector有四个子类,分别是AnimationVector1D, AnimationVector2D, AnimationVector3D 和AnimationVector4D,也就是提供了4个维度的转化。 前面提到的不同类型动画内部其实都是使用的animateValueAsState函数做的封装,比如第一个Float类型的动画,内部代码是:

    return animateValueAsState(
        targetValue,
        Float.VectorConverter, //typeConverter传入的是Float内定义的转换函数
        resolvedAnimSpec,
        visibilityThreshold,
        label,
        finishedListener
    )
    // Float只有一维,因此使用的是AnimationVector1D,像Offset使用的是AnimationVector2D,颜色是AnimationVector4D
    public val Float.Companion.VectorConverter: TwoWayConverter<Float, AnimationVector1D>
    	get() = FloatToVector
    private val FloatToVector: TwoWayConverter<Float, AnimationVector1D> = TwoWayConverter({ AnimationVector1D(it) }, { it.value })

使用:

这里我自定义一个CustomSize类型的动画,包含宽和高,和系统提供的animateSizeAsState做比较看看有没有区别。

// 自定义类型
data class CustomSize(
    val width: Float,
    val height: Float
)
// 编写类型转换逻辑,使用到二维类型AnimationVector2D
val converter = TwoWayConverter<CustomSize, AnimationVector2D>(
    convertToVector = { AnimationVector2D(it.width, it.height) },
    convertFromVector = { CustomSize(it.v1, it.v2) }
)

@Preview
@Composable
fun AnimationExample() {
    var enable by remember { mutableStateOf(false) }
    var isRunning by remember { mutableStateOf(false) }
	// 这是系统提供的Size类型动画函数
    val size by animateSizeAsState(
        targetValue = if (enable) {
            Size(100f, 100f)
        } else {
            Size(200f, 200f)
        },
        animationSpec = tween(durationMillis = 2000),
        finishedListener = {
            isRunning = false
        }
    )
    // 这是自定义类型的动画函数,也是控制大小的
    val customSize by animateValueAsState(
        typeConverter = converter,
        targetValue = if (enable) {
            CustomSize(width = 100f, height = 100f)
        } else {
            CustomSize(width = 200f, height = 200f)
        },
        animationSpec = tween(2000),
        finishedListener = {
            isRunning = false
        }
    )

    Box(
        modifier = Modifier
            .fillMaxSize()
            .padding(10.dp),
        contentAlignment = Alignment.Center
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .align(Alignment.TopCenter)
                .padding(top = 40.dp),
            horizontalArrangement = Arrangement.Center
        ) {
            Box(
                modifier = Modifier
                    .padding(end = 10.dp)
                    //这个方块使用系统的size
                    .size(size.width.dp, size.height.dp) 
                    .background(Color(0xFF4CAF50))
            )
            Box(
                modifier = Modifier
                	//这个方块使用自定义的CustomSize
                    .size(customSize.width.dp, customSize.height.dp) 
                    .background(Color(0xFF00BCD4))
            )
        }
        Button(
            onClick = {
                isRunning = true
                enable = !enable
            },
            modifier = Modifier
                .size(width = 120.dp, height = 40.dp)
                .align(Alignment.Center),
        ) {
            Text(
                text = if (isRunning) "Running" else "Ready",
                color = Color.White
            )
        }
    }
}
效果:

从动图中可以看到自定义CustomSize的和系统封装的Size是一样的效果,去看源码也可以发现系统的代码和我们写的基本一样。 在这里插入图片描述

今日学习到此为止,喜欢的可以点赞收藏,:)