Untitled

 avatar
unknown
kotlin
4 years ago
3.1 kB
4
Indexable
@OptIn(ExperimentalComposeApi::class)
@Composable
fun Android11PinAnimation(modifier: Modifier = Modifier, text: String) {
    val textFlow = parameterToFlow(text)
    Row(
        modifier.fillMaxWidth().height(60.dp),
        verticalAlignment = Alignment.Bottom,
        horizontalArrangement = Arrangement.Center,
    ) {
        text.forEachIndexed { index, symbol ->
            key(index, symbol) {
                val textSize = animatedFloat(0f)
                val bulletSize = animatedFloat(0f)
                var pinToCenter by remember { mutableStateOf(false) }

                launchInComposition {
                    val spring = SpringSpec<Float>(stiffness = Spring.StiffnessLow)
                    textSize.animateTo(1f, spring)
                    textFlow.waitUntil { text -> text.lastIndex != index }
                    delay(600)
                    pinToCenter = true
                    textSize.animateTo(0f, spring)
                    delay(100)
                    bulletSize.animateTo(1f)
                }

                var maxWidth by remember { mutableStateOf(0) }
                val maxWidthDp = with(DensityAmbient.current) { maxWidth.toFloat() / density }

                Box(
                    Modifier
                        .height(54.dp) // 40.sp
                        .onSizeChanged {
                            maxWidth = maxOf(maxWidth, it.width)
                        }
                ) {
                    Spacer(Modifier.width(maxWidthDp.dp))
                    Text(
                        symbol.toString(),
                        Modifier.align(if (pinToCenter) Alignment.Center else Alignment.BottomCenter),
                        fontSize = 40.sp * textSize.value,
                    )
                    Text(
                        "•",
                        Modifier.align(Alignment.Center),
                        fontSize = 40.sp * bulletSize.value,
                    )
                }
            }
        }
    }
}

@Composable
private fun <T> parameterToFlow(parameter: T): Flow<T> {
    return remember { MutableStateFlow(parameter) }.also { it.value = parameter }
}

private suspend fun <T> Flow<T>.waitUntil(condition: (T) -> Boolean) {
    takeWhile { !condition(it) }.collect { }
}

@Composable
@Preview
private fun Android11PinAnimationPreview() {
    Providers(IndicationAmbient provides NoIndication) {
        var text by remember { mutableStateOf("") }
        Android11PinAnimation(
            modifier = Modifier
                .background(Color.Blue.copy(alpha = 0.1f))
                .clickable {
                    text += (text.length % 10).toString()
                    if (text.length > 15) text = text.substring(15)
                },
            text = text
        )
    }
}

private val NoIndication: @Composable () -> Indication = {
    object : Indication {
        override fun createInstance(): IndicationInstance = object : IndicationInstance {
            override fun ContentDrawScope.drawIndication(interactionState: InteractionState) {
                drawContent()
            }
        }
    }
}
Editor is loading...