Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-12 15:07:58 +03:00
parent 44622fd338
commit 79bfe8a57a
2 changed files with 45 additions and 38 deletions

View file

@ -20,13 +20,14 @@ data class BladeAnimation(
@Composable
fun rememberBladeAnimation(): BladeAnimation {
val infiniteTransition = rememberInfiniteTransition()
val infiniteTransition = rememberInfiniteTransition(label = "BladeAnimation")
val offsetState = infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 1500, easing = LinearEasing),
),
label = "BladeOffset",
)
return remember(offsetState) {
@ -37,51 +38,56 @@ fun rememberBladeAnimation(): BladeAnimation {
@Suppress("MagicNumber")
@Composable
fun TextStyle.applyBladeBrush(isEnabled: Boolean, textColor: Color): TextStyle {
return if (isEnabled) {
val offset by LocalBladeAnimation.current.offsetState
val offsetState = LocalBladeAnimation.current.offsetState
val offset = if (isEnabled) offsetState.value else 0f
val brush = remember(offset, textColor) {
object : ShaderBrush() {
override fun createShader(size: Size): Shader {
val center = Offset(size.width / 2f, size.height / 2f)
val diagonal = sqrt(size.width * size.width + size.height * size.height)
// Subtle diagonal angle, similar to iOS shimmer
val direction = Offset(x = 1f, y = 0.3f)
// Half-width of the blob (80% of diagonal total — wide, soft sweep)
val bandHalf = diagonal * 0.40f
// Sweep the highlight center from left-of-element to right-of-element.
// offset 0..1 maps to a full pass including off-screen padding on both sides.
val shift = direction * ((offset - 0.5f) * diagonal * 1.5f)
val highlightCenter = center + shift
// Full color text with a wide, gradual low-alpha dip sweeping left → right
val brush = remember(offset, textColor, isEnabled) {
object : ShaderBrush() {
override fun createShader(size: Size): Shader {
if (!isEnabled) {
// Solid color via the same shader path (LinearGradientShader needs >= 2 colors).
return LinearGradientShader(
colors = listOf(
textColor,
textColor.copy(alpha = 0.75f),
textColor.copy(alpha = 0.45f),
textColor.copy(alpha = 0.3f),
textColor.copy(alpha = 0.45f),
textColor.copy(alpha = 0.75f),
textColor,
),
from = highlightCenter - direction * bandHalf,
to = highlightCenter + direction * bandHalf,
colorStops = listOf(0f, 0.15f, 0.35f, 0.5f, 0.65f, 0.85f, 1f),
colors = listOf(textColor, textColor),
from = Offset.Zero,
to = Offset(size.width, size.height),
tileMode = TileMode.Clamp,
)
}
val center = Offset(size.width / 2f, size.height / 2f)
val diagonal = sqrt(size.width * size.width + size.height * size.height)
// Subtle diagonal angle, similar to iOS shimmer
val direction = Offset(x = 1f, y = 0.3f)
// Half-width of the blob (80% of diagonal total — wide, soft sweep)
val bandHalf = diagonal * 0.40f
// Sweep the highlight center from left-of-element to right-of-element.
// offset 0..1 maps to a full pass including off-screen padding on both sides.
val shift = direction * ((offset - 0.5f) * diagonal * 1.5f)
val highlightCenter = center + shift
// Full color text with a wide, gradual low-alpha dip sweeping left → right
return LinearGradientShader(
colors = listOf(
textColor,
textColor.copy(alpha = 0.75f),
textColor.copy(alpha = 0.45f),
textColor.copy(alpha = 0.3f),
textColor.copy(alpha = 0.45f),
textColor.copy(alpha = 0.75f),
textColor,
),
from = highlightCenter - direction * bandHalf,
to = highlightCenter + direction * bandHalf,
colorStops = listOf(0f, 0.15f, 0.35f, 0.5f, 0.65f, 0.85f, 1f),
tileMode = TileMode.Clamp,
)
}
}
this.copy(brush = brush)
} else {
this.copy(
color = textColor,
)
}
return this.copy(brush = brush)
}
@Preview(showBackground = true)