Android Compose 创建摇动动画

问题描述 投票:0回答:1

我正在尝试在 Jetpack Compose 中制作形状的晃动动画。我想在用户输入无效的 Pin 码时使用此动画来显示错误。但我能找到的只是滑入、滑出动画和一些缩放动画。我有什么想法可以做到这一点吗?

更新: 在@Thracian 回答之后。我使用如下代码,水平摇动我的物品:

fun Modifier.shake(enabled: Boolean, onAnimationFinish: () -> Unit) = composed(
    factory = {
        val distance by animateFloatAsState(
            targetValue = if (enabled) 15f else 0f,
            animationSpec = repeatable(
                iterations = 8,
                animation = tween(durationMillis = 50, easing = LinearEasing),
                repeatMode = RepeatMode.Reverse
            ),
            finishedListener = { onAnimationFinish.invoke() }
        )

        Modifier.graphicsLayer {
            translationX = if (enabled) distance else 0f
        }
    },
    inspectorInfo = debugInspectorInfo {
        name = "shake"
        properties["enabled"] = enabled
    }
)
android android-jetpack-compose android-animation jetpack-compose-animation android-jetpack-compose-animation
1个回答
10
投票

不幸的是,Gif 比实际动画慢,但它给出了结果的想法。

这可以通过多种方式来完成。您应该在短时间内更改scaleX或scaleY或同时更改两者以产生抖动效果。如果您希望旋转,请更改 Modifier.graphicsLayer 的rotationZ

 @Composable
private fun ShakeAnimationSamples() {
    Column(modifier = Modifier
        .fillMaxSize()
        .padding(10.dp)) {

        var enabled by remember {
            mutableStateOf(false)
        }
        val scale by animateFloatAsState(
            targetValue = if (enabled) .9f else 1f,
            animationSpec = repeatable(
                iterations = 5,
                animation = tween(durationMillis = 50, easing = LinearEasing),
                repeatMode = RepeatMode.Reverse
            ),
            finishedListener = {
                enabled = false
            }
        )

        val infiniteTransition = rememberInfiniteTransition()
        val scaleInfinite by infiniteTransition.animateFloat(
            initialValue = 1f,
            targetValue = .85f,
            animationSpec = infiniteRepeatable(
                animation = tween(30, easing = LinearEasing),
                repeatMode = RepeatMode.Reverse
            )
        )

        val rotation by infiniteTransition.animateFloat(
            initialValue = -10f,
            targetValue = 10f,
            animationSpec = infiniteRepeatable(
                animation = tween(30, easing = LinearEasing),
                repeatMode = RepeatMode.Reverse
            )
        )

        Icon(
            imageVector = Icons.Default.NotificationsActive,
            contentDescription = null,
            tint = Color.White,
            modifier = Modifier
                .graphicsLayer {
                    scaleX = if (enabled) scale else 1f
                    scaleY = if (enabled) scale else 1f
                }
                .background(Color.Red, CircleShape)
                .size(50.dp)
                .padding(10.dp)
        )

        Icon(
            imageVector = Icons.Default.NotificationsActive,
            contentDescription = null,
            tint = Color.White,
            modifier = Modifier
                .graphicsLayer {
                    scaleX = scaleInfinite
                    scaleY = scaleInfinite
                    rotationZ = rotation
                }
                .background(Color.Red, CircleShape)
                .size(50.dp)
                .padding(10.dp)
        )



        Button(onClick = { enabled = !enabled }) {
            Text("Animation enabled: $enabled")
        }

    }
}

您也可以将其作为修改器

fun Modifier.shake(enabled: Boolean) = composed(

    factory = {
        
        val scale by animateFloatAsState(
            targetValue = if (enabled) .9f else 1f,
            animationSpec = repeatable(
                iterations = 5,
                animation = tween(durationMillis = 50, easing = LinearEasing),
                repeatMode = RepeatMode.Reverse
            )
        )

        Modifier.graphicsLayer {
            scaleX = if (enabled) scale else 1f
            scaleY = if (enabled) scale else 1f
        }
    },
    inspectorInfo = debugInspectorInfo {
        name = "shake"
        properties["enabled"] = enabled
    }
)

使用方法

Icon(
    imageVector = Icons.Default.NotificationsActive,
    contentDescription = null,
    tint = Color.White,
    modifier = Modifier
        .shake(enabled)
        .background(Color.Red, CircleShape)
        .size(50.dp)
        .padding(10.dp)
)
© www.soinside.com 2019 - 2024. All rights reserved.