我正在尝试在 Jetpack Compose 中创建文本效果,其中文本使用画笔,看起来像是一组沿对角线平滑变化的颜色。文本中的各个字母应该上下移动,以及半圈。
我遇到了两个问题:
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)
)
}
}
这里有两个演示:
如何在 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)
)
}
}
*/
}
您可以通过使用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()
}
}
}