Skip to content

Commit

Permalink
Merge pull request #202 from Infomaniak/higlighted-anim
Browse files Browse the repository at this point in the history
Animate highlighted text
  • Loading branch information
LunarX authored Nov 25, 2024
2 parents f0ba965 + 4077c77 commit 5ceda95
Showing 1 changed file with 46 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@
*/
package com.infomaniak.swisstransfer.ui.components

import androidx.annotation.FloatRange
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
Expand All @@ -43,7 +48,7 @@ import kotlin.math.sin

private val VERTICAL_PADDING @Composable get() = with(LocalDensity.current) { 3.sp.toPx() }
private val HORIZONTAL_PADDING @Composable get() = with(LocalDensity.current) { 10.sp.toPx() }
private const val ROTATION_ANGLE_DEGREE = -3.0f
private const val ROTATION_ANGLE_DEGREE = -3.0

@Composable
fun HighlightedText(
Expand All @@ -52,27 +57,41 @@ fun HighlightedText(
style: TextStyle,
verticalPadding: Float = VERTICAL_PADDING,
horizontalPadding: Float = HORIZONTAL_PADDING,
angleDegrees: Float = ROTATION_ANGLE_DEGREE,
angleDegrees: Double = ROTATION_ANGLE_DEGREE,
) {
val template = stringResource(templateRes)
val argument = stringResource(argumentRes)
val text = String.format(template, argument)

val highlightedColor = SwissTransferTheme.colors.highlightedColor

var highlightedPath by remember { mutableStateOf<Path?>(null) }
var boundingBoxes by remember { mutableStateOf<List<Rect>>(emptyList()) }
var animationStarted by rememberSaveable { mutableStateOf(false) }

LaunchedEffect(Unit) { animationStarted = true }

val highlightProgress by animateFloatAsState(
targetValue = if (animationStarted) 1f else 0f,
animationSpec = tween(
durationMillis = 600,
easing = FastOutSlowInEasing,
delayMillis = 500,
),
label = "Highlighter progress",
)

Text(
text = text,
style = style,
onTextLayout = { layoutResult ->
val boundingBoxes = layoutResult.getArgumentBoundingBoxes(text, argument)
highlightedPath = boundingBoxes.transformForHighlightedStyle(verticalPadding, horizontalPadding, angleDegrees)
},
onTextLayout = { layoutResult -> boundingBoxes = layoutResult.getArgumentBoundingBoxes(text, argument) },
modifier = Modifier.drawBehind {
highlightedPath?.let {
drawPath(path = it, style = Fill, color = highlightedColor)
}
val highlightedPath = boundingBoxes.transformForHighlightedStyle(
verticalPadding,
horizontalPadding,
angleDegrees,
highlightProgress,
)
drawPath(path = highlightedPath, style = Fill, color = highlightedColor)
},
)
}
Expand All @@ -85,14 +104,20 @@ private fun TextLayoutResult.getArgumentBoundingBoxes(text: String, argument: St
private fun List<Rect>.transformForHighlightedStyle(
verticalPadding: Float,
horizontalPadding: Float,
angleDegrees: Float,
angleDegrees: Double,
@FloatRange(0.0, 1.0) progress: Float,
): Path = Path().apply {
forEach { boundingBox ->
addPath(boundingBox.transformForHighlightedStyle(verticalPadding, horizontalPadding, angleDegrees))
addPath(boundingBox.transformForHighlightedStyle(verticalPadding, horizontalPadding, angleDegrees, progress))
}
}

private fun Rect.transformForHighlightedStyle(verticalPadding: Float, horizontalPadding: Float, angleDegrees: Float): Path {
private fun Rect.transformForHighlightedStyle(
verticalPadding: Float,
horizontalPadding: Float,
angleDegrees: Double,
@FloatRange(0.0, 1.0) progress: Float,
): Path {
return getRotatedRectanglePath(
Rect(
left = left - horizontalPadding,
Expand All @@ -101,14 +126,15 @@ private fun Rect.transformForHighlightedStyle(verticalPadding: Float, horizontal
bottom = bottom + verticalPadding,
),
angleDegrees = angleDegrees,
progress = progress,
)
}

private fun getRotatedRectanglePath(rect: Rect, angleDegrees: Float): Path {
private fun getRotatedRectanglePath(rect: Rect, angleDegrees: Double, @FloatRange(0.0, 1.0) progress: Float): Path {
val centerX = rect.left + (rect.width / 2)
val centerY = rect.top + (rect.height / 2)

val angleRadians = Math.toRadians(angleDegrees.toDouble())
val angleRadians = Math.toRadians(angleDegrees)

// Function to rotate a point around the center
fun rotatePoint(x: Float, y: Float, centerX: Float, centerY: Float, angleRadians: Double): Offset {
Expand All @@ -121,9 +147,12 @@ private fun getRotatedRectanglePath(rect: Rect, angleDegrees: Float): Path {
return Offset(newX, newY)
}

val width = rect.right - rect.left
val right = rect.left + progress * width

val topLeft = rotatePoint(rect.left, rect.top, centerX, centerY, angleRadians)
val topRight = rotatePoint(rect.right, rect.top, centerX, centerY, angleRadians)
val bottomRight = rotatePoint(rect.right, rect.bottom, centerX, centerY, angleRadians)
val topRight = rotatePoint(right, rect.top, centerX, centerY, angleRadians)
val bottomRight = rotatePoint(right, rect.bottom, centerX, centerY, angleRadians)
val bottomLeft = rotatePoint(rect.left, rect.bottom, centerX, centerY, angleRadians)

return Path().apply {
Expand Down

0 comments on commit 5ceda95

Please sign in to comment.