kotlin 如何动画一个圆形的背景颜色为`组合`?

f0brbegy  于 2023-05-29  发布在  Kotlin
关注(0)|答案(1)|浏览(212)

我的目标是有一个Composable fun(或Modifier扩展),我可以从另一个Composable方法调用它,这个方法将添加一个圆形的红色背景。背景应该以10%的速度缓慢增长和收缩。
这就是它现在的样子:

红色背景不动画,我想知道我的代码有什么问题。内容本身应该保持不变,红色背景应该只增长和缩小:

@Composable
fun PulsatingCircle(content: @Composable () -> Unit) {
    val infiniteTransition = rememberInfiniteTransition()
    val scale by infiniteTransition.animateFloat(
        initialValue = 1f,
        targetValue = 2f, // 2 to see if it does really something quickly
        animationSpec = infiniteRepeatable(
            animation = tween(1700, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        )
    )

    Box(
        modifier = Modifier
            .layout { measurable, constraints ->
                val placeable = measurable.measure(constraints)
                val currentHeight = placeable.height
                val currentWidth = placeable.width
                val newDiameter = maxOf(currentHeight, currentWidth)
                val newScale = newDiameter * scale

                layout(newScale.toInt(), newScale.toInt()) {
                    placeable.placeRelative(
                        ((newScale - currentWidth) / 2).toInt(),
                        ((newScale - currentHeight) / 2).toInt()
                    )
                }
            }
            .background(color = Color.Red, shape = CircleShape)
    ) {
        content()
    }
}
cyvaqqii

cyvaqqii1#

如果使用Layout在更改大小的同时移动兄弟节点Modifier.background()的目的应该在Modifier.layout之前,但这可以很容易地设置动画Modifier.drawBehind{drawCircle(Color.Red,radius = size*scale)}
如果您不想让内容在带洋红边框的父对象增长时移动,您也可以使用“布局”

@Composable
fun PulsatingCircle(content: @Composable () -> Unit) {
    val infiniteTransition = rememberInfiniteTransition(label = "")
    val scale by infiniteTransition.animateFloat(
        initialValue = 1f,
        targetValue = 2f, // 2 to see if it does really something quickly
        animationSpec = infiniteRepeatable(
            animation = tween(1700, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ), label = ""
    )

    Box(
        modifier = Modifier
            .border(1.dp, Color.Magenta)
            .background(color = Color.Red, shape = CircleShape)
            .layout { measurable, constraints ->
                val placeable = measurable.measure(constraints)
                val currentHeight = placeable.height
                val currentWidth = placeable.width
                val newDiameter = maxOf(currentHeight, currentWidth)
                val newScale = newDiameter * scale
                layout(newScale.toInt(), newScale.toInt()) {
                    placeable.placeRelative(
                        ((newScale - currentWidth) / 2).toInt(),
                        ((newScale - currentHeight) / 2).toInt()
                    )
                }
            }

            .border(2.dp, Color.Blue)

    ) {
        content()
    }
}

@Composable
fun PulsatingCircle2(content: @Composable () -> Unit) {
    val infiniteTransition = rememberInfiniteTransition(label = "")
    val scale by infiniteTransition.animateFloat(
        initialValue = 1f,
        targetValue = 2f, // 2 to see if it does really something quickly
        animationSpec = infiniteRepeatable(
            animation = tween(1700, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ), label = ""
    )

    var offset by remember {
        mutableStateOf(IntOffset(0, 0))
    }

    Box(
        modifier = Modifier
            .border(2.dp, Color.Magenta)
            .offset {
                offset
            }
            .background(color = Color.Red, shape = CircleShape)
            .layout { measurable, constraints ->
                val placeable = measurable.measure(constraints)
                val currentHeight = placeable.height
                val currentWidth = placeable.width
                val newDiameter = maxOf(currentHeight, currentWidth)
                val newScale = newDiameter * scale
                offset = IntOffset(
                    -((newScale - currentWidth) / 2).toInt(),
                    -((newScale - currentHeight) / 2).toInt()
                )
                layout(newScale.toInt(), newScale.toInt()) {
                    placeable.placeRelative(
                        ((newScale - currentWidth) / 2).toInt(),
                        ((newScale - currentHeight) / 2).toInt()
                    )
                }
            }

            .border(2.dp, Color.Blue)

    ) {
        content()
    }
}

使用修饰符.drawBehind{}

fun Modifier.animateBackground(
    color: Color
) = composed {
    val infiniteTransition = rememberInfiniteTransition(label = "")
    val scale by infiniteTransition.animateFloat(
        initialValue = 1f,
        targetValue = 2f, // 2 to see if it does really something quickly
        animationSpec = infiniteRepeatable(
            animation = tween(1700, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ), label = ""
    )

    Modifier.drawBehind {
        val radius = (size.width.coerceAtLeast(size.height)) / 2
        drawCircle(color = color, radius = radius * scale)
    }
}

Demo

@Preview
@Composable
private fun Test() {
    Column(
        modifier = Modifier.padding(top = 30.dp, start = 30.dp)

    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .border(2.dp, Color.Cyan)
        ) {
            PulsatingCircle {
                Image(
                    imageVector = Icons.Default.People,
                    contentDescription = null
                )
            }

            Box(
                modifier = Modifier
                    .size(100.dp)
                    .background(Color.Yellow)
            )
        }

        Spacer(modifier = Modifier.height(10.dp))
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .border(2.dp, Color.Cyan)
        ) {
            PulsatingCircle2 {
                Image(
                    imageVector = Icons.Default.People,
                    contentDescription = null
                )
            }

            Box(
                modifier = Modifier
                    .size(100.dp)
                    .background(Color.Yellow)
            )
        }

        Spacer(modifier = Modifier.height(10.dp))
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .border(2.dp, Color.Cyan)
        ) {
            Image(
                modifier = Modifier
                    .zIndex(4f)
                    .animateBackground(Color.Red),
                imageVector = Icons.Default.People,
                contentDescription = null
            )

            Box(
                modifier = Modifier
                    .size(100.dp)
                    .background(Color.Yellow)
            )
        }
    }
}

相关问题