在 Jetpack Compose 中单独为每个字母设置动画时对整个单词应用画笔效果

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

我正在尝试在 Jetpack Compose 中创建文本效果,其中文本使用画笔,看起来像是一组沿对角线平滑变化的颜色。文本中的各个字母应该上下移动,以及半圈。

我遇到了两个问题:

  1. 如果我尝试将画笔应用于整个单词,我不知道如何 为移动和旋转的字母设置动画。这是我的代码 尝试过:
        Text(
        text = word,
        fontSize = fontSize,
        style = LocalTextStyle.current.copy(brush = gradientBrush),
        modifier = modifier
    )
  1. 如果我将动画单独应用于每个字母,我可以实现 动画的预期结果,但画笔应用于 每个字母也都是单独的,从而产生不吸引人的效果。我 需要用画笔涂抹到整个单词。这是我的代码 尝试过:
    Row {
        word.forEachIndexed { index, char ->
            val angle by rotationAngles[index]
            val offset by letterOffsets[index]
            Text(
                text = char.toString(),
                fontSize = fontSize,
                style = LocalTextStyle.current.copy(brush = gradientBrush),
                modifier = Modifier
                    .rotate(angle)
                    .offset(y = offset.dp)
            )
        }
    }

这里有两个演示:

  1. 将画笔应用于整个单词的预期结果,但我不知道如何对字母进行动画处理。
  2. 应用动画的预期结果,但我无法将画笔应用到整个单词(画笔单独应用到每个字母)。

如何在 Jetpack Compose 中将画笔应用于整个单词时,为每个字母单独创建向上、向下和半转的动画?

完整代码如下:

@Preview(showBackground = true)
@Composable
fun RotatingLettersPreview() {
    RotatingLetters("Medium")
}

@OptIn(ExperimentalTextApi::class)
@Composable
fun RotatingLetters(word: String,
                    fontSize: TextUnit = TextUnit.Unspecified,
                    modifier: Modifier = Modifier) {

    val colorGradient = listOf(
        Color(0xFFC0EFFF), // Light blue
        Color(0xFF9BDBFB), // Medium blue
        Color(0xFF75C2F9), // Dark blue
        Color(0xFFF7D7C4), // Light pink
        Color(0xFFF5B2C7), // Medium pink
        Color(0xFFF28BAE), // Dark pink
        Color(0xFFF7F2D4), // Light beige
        Color(0xFFF5E0C3), // Medium beige
    )

    val fontSizeInPx = with(LocalDensity.current) { 30.sp.toPx() }
    val doubleFontSizeInPx = fontSizeInPx * 2

    val infiniteTransitionForOffset = rememberInfiniteTransition(label = "")
    val offsetAnimation by infiniteTransitionForOffset.animateFloat(
        initialValue = 0f,
        targetValue = doubleFontSizeInPx,
        animationSpec = infiniteRepeatable(tween(200000, easing = LinearEasing)), label = ""
    )

    val gradientBrush = remember(offsetAnimation) {
        object : ShaderBrush() {
            override fun createShader(size: Size): Shader {
                val widthOffset = size.width * offsetAnimation
                val heightOffset = size.height * offsetAnimation
                return LinearGradientShader(
                    colors = colorGradient,
                    from = Offset(widthOffset, heightOffset),
                    to = Offset(widthOffset + size.width, heightOffset + size.height),
                    tileMode = TileMode.Mirror
                )
            }
        }
    }

    val infiniteTransitionForRotation = rememberInfiniteTransition(label = "")
    val rotationDirection = remember { mutableStateOf(1) }

    val rotationAngles = List(word.length) { index ->
        infiniteTransitionForRotation.animateFloat(
            initialValue = 0f,
            targetValue =
            if (index % 2 == 0) 5f * rotationDirection.value else -5f * rotationDirection.value,
            animationSpec = infiniteRepeatable(
                animation = tween(250, easing = LinearEasing),
                repeatMode = RepeatMode.Restart
            ), label = ""
        )
    }

    LaunchedEffect(key1 = Unit) {
        while (true) {
            delay(250)
            rotationDirection.value *= -1
        }
    }

    val letterOffsets = List(word.length) { index ->
        infiniteTransitionForRotation.animateFloat(
            initialValue = 0f,
            targetValue = if (index % 2 == 0) 2f else -2f,
            animationSpec = infiniteRepeatable(
                animation = tween(250, easing = LinearEasing),
                repeatMode = RepeatMode.Reverse
            ), label = ""
        )
    }

    Text(
        text = word,
        fontSize = fontSize,
        style = LocalTextStyle.current.copy(brush = gradientBrush),
        modifier = modifier
    )

/*
    Row {
        word.forEachIndexed { index, char ->
            val angle by rotationAngles[index]
            val offset by letterOffsets[index]
            Text(
                text = char.toString(),
                fontSize = fontSize,
                style = LocalTextStyle.current.copy(brush = gradientBrush),
                modifier = Modifier
                    .rotate(angle)
                    .offset(y = offset.dp)
            )
        }
    }
*/
}
android-jetpack-compose android-jetpack jetpack-compose-animation android-jetpack-compose-animation
1个回答
0
投票

您可以通过使用drawWithContent和剪切每个字母的组合来实现类似的效果,以在所有字母上创建单个连续渐变。这是一个概念性的解决方案:

创建一个使用 Canvas 手动绘制文本的自定义可组合项,允许您在整个单词上应用单个渐变画笔。 在绘制字符时,使用drawWithContent将绘图区域剪切到每个字符的边界,这样它们就可以独立地制作动画,同时仍然共享相同的画笔。 以下是如何实现它的示例:

@Composable

fun AnimatedGradientText(word:String,gradientBrush:Brush,fontSize:TextUnit,modifier:Modifier = Modifier){ // 计算文本大小、字符位置等 val textPaint = 记住 { Paint().asFrameworkPaint().apply { isAntiAlias = true 样式 = android.graphics.Paint.Style.FILL } }

// For each letter, create a rotation and offset animation.
val rotationAngles = remember { /* ... Your rotation animations ... */ }
val letterOffsets = remember { /* ... Your offset animations ... */ }

Canvas(modifier = modifier) {
    // Calculate text size and positions
    val textLayoutResult = remember(word, fontSize) { /* ... Measure text ... */ }

    word.forEachIndexed { index, char ->
        // Get animation values
        val angle: Float by rotationAngles[index]
        val offset: Float by letterOffsets[index]

        // Save the current canvas state
        save()

        // Position the canvas for this character
        translate(left + characterPositions[index], top)

        // Apply the rotation transformation
        rotate(angle)

        // Set the shader to the paint
        textPaint.shader = gradientBrush.asAndroidShader()

        // Draw this character
        drawContext.canvas.nativeCanvas.drawText(
            char.toString(),
            0f,
            0f + offset,
            textPaint
        )

        // Restore the canvas to avoid affecting subsequent characters
        restore()
    }
}

}

© www.soinside.com 2019 - 2024. All rights reserved.