如何为`Composable`创建一个圆形背景颜色的动画?

huangapple go评论61阅读模式
英文:

How to animate a circular background color for a `Composable`?

问题

Here's the translated code portion:

@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()
    }
}

Please note that I've translated the code portion only, as per your request. If you have any specific questions or need further assistance with this code, feel free to ask.

英文:

My goal is to have a Composable fun (or Modifier extension) which I can call from another Composable method which will add a circular red background to it. The background should grow and shrink slowly by 10%.

This is what it looks now:
如何为`Composable`创建一个圆形背景颜色的动画?

The red background does not animate and I would like to know what is wrong with my code. The content itself should remain the same, the red background should only grow and shrink:

@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()
    }
}

答案1

得分: 2

如果在更改大小的同时使用 Layout 移动同级元素的目的是使用 Modifier.drawBehind{} 在 Modifier.layout 之前,但这可以很容易地通过动画实现 Modifier.drawBehind{drawCircle(Color.Red, radius = size*scale)}

使用 Layout 时,您还可以使用 Offset,以便在父级具有品红边框并增长时,内容不会移动。

对于 Modifier.drawBehind{},您可以创建一个函数 animateBackground,并在其内部使用 drawBehind 绘制动画背景。

演示代码中,通过 PulsatingCirclePulsatingCircle2 函数以及 animateBackground 函数,展示了动画效果和不同布局的使用。

演示图链接

英文:

If the purpose of using Layout to move siblings while changing size Modifier.background() should be before Modifier.layout but this can be easily animated Modifier.drawBehind{drawCircle(Color.Red, radius = size*scale)}

With Layout you can use Offset too If you don't want content to move while parent with Magenta border is growing as

如何为`Composable`创建一个圆形背景颜色的动画?

@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()
    }
}

With Modifier.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)
            )
        }
    }
}

huangapple
  • 本文由 发表于 2023年5月24日 23:14:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76325044.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定