Jetpack Compose 中的缩放按钮动画

时间:2021-02-18 00:03:26

标签: android android-jetpack-compose

我想用 Jetpack Compose 从 AirBnB 应用程序中创建这个很棒的按钮动画 1

不幸的是,动画/过渡 API 最近发生了变化,而且几乎没有相关文档。有人可以帮我找到实现此按钮按下动画的正确方法吗?

编辑 根据@Amirhosein 的回答,我开发了一个按钮,看起来几乎与 Airbnb 示例一模一样

代码:

@Composable
fun AnimatedButton() {
    val boxHeight = animatedFloat(initVal = 50f)
    val relBoxWidth = animatedFloat(initVal = 1.0f)
    val fontSize = animatedFloat(initVal = 16f)

    fun animateDimensions() {
        boxHeight.animateTo(45f)
        relBoxWidth.animateTo(0.95f)
       // fontSize.animateTo(14f)
    }

    fun reverseAnimation() {
        boxHeight.animateTo(50f)
        relBoxWidth.animateTo(1.0f)
        //fontSize.animateTo(16f)
    }

        Box(
        modifier = Modifier
            .height(boxHeight.value.dp)
            .fillMaxWidth(fraction = relBoxWidth.value)

            .clip(RoundedCornerShape(8.dp))
            .background(Color.Black)
            .clickable { }
            .pressIndicatorGestureFilter(
                onStart = {
                    animateDimensions()
                },
                onStop = {
                    reverseAnimation()
                },
                onCancel = {
                    reverseAnimation()
                }
            ),
        contentAlignment = Alignment.Center
    ) {
        Text(text = "Explore Airbnb", fontSize = fontSize.value.sp, color = Color.White)
    }
}

视频:

2

不幸的是,我无法弄清楚如何正确地为文本设置动画,因为它目前看起来很糟糕

4 个答案:

答案 0 :(得分:3)

使用 pressIndicatorGestureFilter 实现此行为。

这是我的解决方法:

@Preview
@Composable
fun MyFancyButton() {
val boxHeight = animatedFloat(initVal = 60f)
val boxWidth = animatedFloat(initVal = 200f)
    Box(modifier = Modifier
        .height(boxHeight.value.dp)
        .width(boxWidth.value.dp)
        .clip(RoundedCornerShape(4.dp))
        .background(Color.Black)
        .clickable { }
        .pressIndicatorGestureFilter(
            onStart = {
                boxHeight.animateTo(55f)
                boxWidth.animateTo(180f)
            },
            onStop = {
                boxHeight.animateTo(60f)
                boxWidth.animateTo(200f)
            },
            onCancel = {
                boxHeight.animateTo(60f)
                boxWidth.animateTo(200f)
            }
        ), contentAlignment = Alignment.Center) {
           Text(text = "Utforska Airbnb", color = Color.White)
     }
}

默认的 jetpack compose Button 在其 onClick 事件中使用点击手势,而 pressIndicatorGestureFilter 不接收点击。这就是我创建这个自定义按钮的原因

答案 1 :(得分:2)

你在寻找这样的东西吗?

@Composable
fun AnimatedButton() {
    val selected = remember { mutableStateOf(false) }
    val scale = animateFloatAsState(if (selected.value) 2f else 1f)

    Column(
        Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Button(
            onClick = {  },
            modifier = Modifier
                .scale(scale.value)
                .height(40.dp)
                .width(200.dp)
                .pointerInteropFilter {
                    when (it.action) {
                        MotionEvent.ACTION_DOWN -> {
                            selected.value = true }

                        MotionEvent.ACTION_UP  -> {
                           selected.value = false }
                    }
                    true
                }
        ) {
            Text(text = "Explore Airbnb", fontSize = 15.sp, color = Color.White)
        }
    }
}

答案 2 :(得分:1)

通过 1.0.0-beta06,您可以使用 Modifier.pointerInput 来检测 tapGesture。
定义一个枚举:

enum class ComponentState { Pressed, Released }

那么:

var toState by remember { mutableStateOf(ComponentState.Released) }
val modifier = Modifier.pointerInput(Unit) {
    detectTapGestures(
        onPress = {
            toState = ComponentState.Pressed
            tryAwaitRelease()
            toState = ComponentState.Released
        }

    )
}
 // Defines a transition of `ComponentState`, and updates the transition when the provided [targetState] changes
val transition: Transition<ComponentState> = updateTransition(targetState = toState, label = "")

// Defines a float animation to scale x,y
val scalex: Float by transition.animateFloat(
    transitionSpec = { spring(stiffness = 50f) }, label = ""
) { state ->
    if (state == ComponentState.Pressed) 1.25f else 1f
}
val scaley: Float by transition.animateFloat(
    transitionSpec = { spring(stiffness = 50f) }, label = ""
) { state ->
    if (state == ComponentState.Pressed) 1.05f else 1f
}

应用 modifier 并使用 Modifier.graphicsLayer 还可以更改文本尺寸。

Box(
    modifier
        .padding(16.dp)
        .width((100 * scalex).dp)
        .height((50 * scaley).dp)
        .background(Color.Black, shape = RoundedCornerShape(8.dp)),
    contentAlignment = Alignment.Center) {

        Text("BUTTON", color = Color.White,
            modifier = Modifier.graphicsLayer{
                scaleX = scalex;
                scaleY = scaley
            })

}

enter image description here

答案 3 :(得分:0)

如果你想用不同类型的动画制作按钮动画,比如缩放、旋转和许多不同类型的动画,那么你可以在 jetpack compose 中使用这个库。 Check Here