likes
comments
collection
share

Jetpack Compose - 动画的几种结束机制 (九)

作者站长头像
站长
· 阅读数 27

动画的打断机制

所谓动画的打断机制 其实就是当某一个anim 在执行的时候,如果这个anim又跑去执行了其他的动画 那么之前的动画就会被打断, 注意是打断,而不是等前面一个动画执行完了再执行下一个

可以参考下面的这段代码, 本来我这个box 是要惯性滑动到某个位置的, 但是因为我在1100的时候 又开始了 另外一个anim的操作 所以在滑动到那个位置之前 我就往回惯性滑动了

也就是说在1000ms 开始执行的动画 被打断了,我们可以在try catch中 捕获到这个被打断的异常

setContent {
    val anim = remember {
        Animatable(0.dp, Dp.VectorConverter)
    }

    val decay = rememberSplineBasedDecay<Dp>()
    LaunchedEffect(Unit) {
        delay(1000)
        // 动画的监听
        try {
            anim.animateDecay(3000.dp, decay)
        } catch (e: CancellationException) {
            Log.e("wuyue","cancel my anim")
        }
    }

    LaunchedEffect(Unit) {
        delay(1100)
        // 动画的监听
        anim.animateDecay((-1000).dp, decay)
    }


    Box(
        Modifier
            .padding(0.dp, anim.value, 0.dp, 0.dp)
            .size(100.dp)
            .background(Color.Green)
    ) {

    }


}

下面这3个函数 会出现互相打断的情况

anim.animateDecay()
anim.animateTo()
anim.snapTo()

动画的停止

在某些时候,我们需要对某个进行中的动画发出信号让他停止执行

.clickable {
    lifecycleScope.launch{
        anim.stop()
    }
}

在compose中 我们可以方便的使用stop函数 即可,注意了 这个stop和anim函数一样 都是需要协程的执行环境的, 所以我们需要给他指定一个协程环境的scope

有人要问了 为啥这里不用launedEffect ,le 也是一个scope啊,

因为通常而言 le的环境是和compose的生命周期绑定在一起的,当你不想在compose重组的时候 重复执行某些操作 那就可以用 le,其他时候 其实不用le 更好

动画的边界条件

看下面这个代码

setContent {
    val anim = remember {
        Animatable(0.dp, Dp.VectorConverter)
    }

    val decay = rememberSplineBasedDecay<Dp>()
    LaunchedEffect(Unit) {
        delay(1000)
        // 动画的监听
        try {
            anim.animateDecay(3000.dp, decay)
        } catch (e: CancellationException) {
            Log.e("wuyue","cancel my anim")
        }
    }
    Box(
        Modifier
            .padding(anim.value, 0.dp, 0.dp, 0.dp)
            .size(100.dp)
            .background(Color.Green)
            .clickable {
                lifecycleScope.launch{
                    anim.stop()
                }
            }
    ) {

    }


}

同样是动画执行,但是这次 我们改变的是 paddingleft, 执行以后你会发现这个方块 出了屏幕了看不到了?

但是往往我们需要的是 想让这个方块在滑动的时候 到屏幕边缘就停止

所以我们需要给 动画设置一个边界条件,让他到了边界条件的时候就自动停止

那么显然 在这里 我们的边界条件就是让这个方块 滑动到屏幕边缘就立即结束 不要继续

进行如下修改即可

setContent {
    BoxWithConstraints {
        val anim = remember {
            Animatable(0.dp, Dp.VectorConverter)
        }

        val decay = rememberSplineBasedDecay<Dp>()
        LaunchedEffect(Unit) {
            delay(1000)
            // 动画的监听
            try {
                anim.animateDecay(3000.dp, decay)
            } catch (e: CancellationException) {
                Log.e("wuyue","cancel my anim")
            }
        }

        anim.updateBounds(upperBound = maxWidth - 100.dp)
        Box(
            Modifier
                .padding(anim.value, 0.dp, 0.dp, 0.dp)
                .size(100.dp)
                .background(Color.Green)
                .clickable {
                    lifecycleScope.launch {
                        anim.stop()
                    }
                }
        ) {

        }

    }


}

这里要注意的是 anim.updateBounds(upperBound = maxWidth - 100.dp)

这行代码madxWidth是BoxWithConstraints他的一个默认属性,我们可以直接使用, 为啥要-100.dp 这里要想明白

Jetpack Compose - 动画的几种结束机制 (九)

因为你想要的其实是 这个方块的右边距 顶着右边屏幕, 而我们一个view的坐标 是这个view的左顶点,所以 你的动画边距。最后算出来的距离 就必须得是 屏幕宽度-box的宽度

动画的返回值

还是上面的代码 ,我们稍微做一下修改, 给动画的执行加一个返回值

LaunchedEffect(Unit) {
    delay(1000)
    // 动画的监听
    try {
        val result = anim.animateDecay(3000.dp, decay)
        if (result.endReason == AnimationEndReason.BoundReached) {
            Log.e("wuyue","finish because endReason is boundReached")

        }
    } catch (e: CancellationException) {
        Log.e("wuyue","cancel my anim")
    }
}

再看一下 动画执行到屏幕边缘以后的日志

Jetpack Compose - 动画的几种结束机制 (九)

这里能明确的看出来

到边缘停止的情况,是不会有异常的,反而我们还能在动画执行的返回值中 判断到这种情况

我们甚至可以利用这个方便的返回值,来很方便的实现 反弹效果

LaunchedEffect(Unit) {
    delay(1000)
    // 动画的监听
    try {
        var result = anim.animateDecay(3000.dp, decay)
        while (result.endReason == AnimationEndReason.BoundReached) {
            result = anim.animateDecay(-result.endState.velocity, decay)
        }
    } catch (e: CancellationException) {
    }
}

anim.updateBounds(upperBound = maxWidth - 100.dp, lowerBound = 0.dp)
转载自:https://juejin.cn/post/7160878990347993101
评论
请登录