animation runs forever

This commit is contained in:
callebtc
2025-08-30 16:28:21 +02:00
parent 9bb9c2e89d
commit 9d9ee1e413
@@ -41,55 +41,31 @@ fun AnimatedMatrixText(
fontSize: TextUnit = TextUnit.Unspecified, fontSize: TextUnit = TextUnit.Unspecified,
fontWeight: FontWeight? = null fontWeight: FontWeight? = null
) { ) {
// Animation state for each character // Continuous animation: loop random characters until isAnimating becomes false
var animatedText by remember(targetText) { mutableStateOf(targetText) } var animatedText by remember(targetText) { mutableStateOf(targetText) }
var characterStates by remember(targetText) { mutableStateOf(
targetText.indices.map { CharacterAnimationState.ENCRYPTED }.toList()
) }
// Start animation when isAnimating becomes true
LaunchedEffect(isAnimating, targetText) { LaunchedEffect(isAnimating, targetText) {
if (isAnimating && targetText.isNotEmpty()) { if (isAnimating && targetText.isNotEmpty()) {
// Reset character states for new animation val encryptedChars = "!@#$%^&*()_+-=[]{}|;:,.<>?~`".toCharArray()
characterStates = targetText.indices.map { CharacterAnimationState.ENCRYPTED }.toList() while (true) {
val frame = CharArray(targetText.length) {
// Animate all characters in parallel with staggered start times encryptedChars[Random.nextInt(encryptedChars.size)]
targetText.forEachIndexed { index, targetChar ->
launch { // Use the LaunchedEffect's CoroutineScope
// Stagger character animations (50ms per character like the JS example)
delay(index * 50L)
animateCharacter(
index = index,
targetChar = targetChar,
currentText = { animatedText },
updateText = { newText -> animatedText = newText },
updateState = { newStates -> characterStates = newStates }
)
} }
animatedText = String(frame)
delay(80L)
} }
} else { } else {
// Not animating, show final text immediately
animatedText = targetText animatedText = targetText
characterStates = targetText.indices.map { CharacterAnimationState.FINAL }.toList()
} }
} }
// Build annotated string with proper styling for each character state
val styledText = buildAnnotatedString { val styledText = buildAnnotatedString {
animatedText.forEachIndexed { index, char -> animatedText.forEach { char ->
val state = characterStates.getOrNull(index) ?: CharacterAnimationState.FINAL
val charColor = when (state) {
CharacterAnimationState.ENCRYPTED -> Color(0xFF00FF41) // Matrix green
CharacterAnimationState.DECRYPTING -> Color(0xFFFFFF41) // Yellow transition
CharacterAnimationState.FINAL -> color
}
withStyle( withStyle(
style = SpanStyle( style = SpanStyle(
color = charColor, color = color,
fontFamily = FontFamily.Monospace, fontFamily = FontFamily.Monospace,
fontWeight = if (state == CharacterAnimationState.ENCRYPTED) FontWeight.Bold else fontWeight fontWeight = fontWeight
) )
) { ) {
append(char) append(char)
@@ -283,36 +259,19 @@ private fun AnimatedMessageDisplay(
// Update animated content when animation state changes // Update animated content when animation state changes
LaunchedEffect(isAnimating, message.content) { LaunchedEffect(isAnimating, message.content) {
if (isAnimating && message.content.isNotEmpty()) { if (isAnimating && message.content.isNotEmpty()) {
// Start with encrypted content
val encryptedChars = "!@#$%^&*()_+-=[]{}|;:,.<>?~`".toCharArray() val encryptedChars = "!@#$%^&*()_+-=[]{}|;:,.<>?~`".toCharArray()
val baseLength = message.content.length
// Animate each character gradually // Loop animation continuously until PoW stops (LaunchedEffect will cancel on recomposition)
message.content.forEachIndexed { index, targetChar -> while (true) {
launch { // Generate a new random frame of the same length as the real content
// Stagger character animations val frame = CharArray(baseLength) {
delay(index * 50L) encryptedChars[Random.nextInt(encryptedChars.size)]
val animationDuration = Random.nextLong(1000, 3000)
val startTime = System.currentTimeMillis()
// Phase 1: Random encrypted characters
while (System.currentTimeMillis() - startTime < animationDuration) {
val randomChar = encryptedChars[Random.nextInt(encryptedChars.size)]
val currentText = animatedContent.toCharArray()
if (index < currentText.size) {
currentText[index] = randomChar
animatedContent = String(currentText)
}
delay(100)
}
// Phase 2: Reveal final character
val finalText = animatedContent.toCharArray()
if (index < finalText.size) {
finalText[index] = targetChar
animatedContent = String(finalText)
}
} }
animatedContent = String(frame)
// Small delay for smooth animation; adjust if needed
delay(80L)
} }
} else { } else {
// Not animating, show final content // Not animating, show final content