From 80f5e436ee9706548a56383654d3fbc1cc3da414 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Thu, 15 Jan 2026 22:40:24 +0500 Subject: [PATCH] feat: Optimize onboarding animations and improve page swiping performance --- .../ui/onboarding/OnboardingScreen.kt | 242 ++++++++---------- 1 file changed, 104 insertions(+), 138 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/ui/onboarding/OnboardingScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/onboarding/OnboardingScreen.kt index dad45dc..291fc0f 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/onboarding/OnboardingScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/onboarding/OnboardingScreen.kt @@ -247,15 +247,17 @@ fun OnboardingScreen( modifier = Modifier .fillMaxWidth() .height(150.dp), + // Pre-load adjacent pages for smooth swiping + beyondBoundsPageCount = 2, flingBehavior = PagerDefaults.flingBehavior( state = pagerState, lowVelocityAnimationSpec = tween( - durationMillis = 300, + durationMillis = 250, easing = FastOutSlowInEasing ), snapAnimationSpec = spring( - dampingRatio = Spring.DampingRatioLowBouncy, - stiffness = Spring.StiffnessLow + dampingRatio = Spring.DampingRatioNoBouncy, + stiffness = Spring.StiffnessMedium ) ) ) { page -> @@ -372,42 +374,20 @@ fun AnimatedRosettaLogo( bookComposition: Any?, modifier: Modifier = Modifier ) { - val currentPage = pagerState.currentPage - val pageOffset = pagerState.currentPageOffsetFraction - - // Animate scale and alpha based on swipe - val scale by animateFloatAsState( - targetValue = 1f - (pageOffset.absoluteValue * 0.08f), - animationSpec = tween(400, easing = FastOutSlowInEasing), - label = "scale" - ) - - val alpha by animateFloatAsState( - targetValue = 1f - (pageOffset.absoluteValue * 0.3f), - animationSpec = tween(400, easing = FastOutSlowInEasing), - label = "alpha" - ) + // Use derivedStateOf for optimized reads + val currentPage by remember { derivedStateOf { pagerState.currentPage } } Box( - modifier = modifier - .graphicsLayer { - // Enable hardware acceleration - compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen - }, + modifier = modifier, contentAlignment = Alignment.Center ) { - // Pre-render all animations to avoid lag - Box( - modifier = Modifier - .fillMaxSize() - .graphicsLayer { - // Hardware layer for better performance - renderEffect = null - }, - contentAlignment = Alignment.Center + // Rosetta icon (page 0) with pulse animation like splash screen + androidx.compose.animation.AnimatedVisibility( + visible = currentPage == 0, + enter = fadeIn(animationSpec = tween(200)), + exit = fadeOut(animationSpec = tween(200)) ) { - // Rosetta icon (page 0) with pulse animation like splash screen - if (currentPage == 0) { + Box(contentAlignment = Alignment.Center) { val pulseScale by rememberInfiniteTransition(label = "pulse").animateFloat( initialValue = 1f, targetValue = 1.1f, @@ -418,7 +398,7 @@ fun AnimatedRosettaLogo( label = "pulseScale" ) - // Glow effect behind logo - same style as splash screen + // Glow effect behind logo Box( modifier = Modifier .size(180.dp) @@ -429,7 +409,7 @@ fun AnimatedRosettaLogo( ) ) - // Main logo - circular like splash screen + // Main logo Image( painter = painterResource(id = R.drawable.rosetta_icon), contentDescription = "Rosetta Logo", @@ -438,111 +418,97 @@ fun AnimatedRosettaLogo( .clip(CircleShape) ) } - - // Fast page - idea animation (page 1) - // Load adjacent pages for smooth swiping - val shouldLoadIdea = currentPage in 0..2 - if (shouldLoadIdea) { - ideaComposition?.let { comp -> - val lottieComp = comp as? com.airbnb.lottie.LottieComposition - val progress by animateLottieCompositionAsState( - composition = lottieComp, - iterations = 1, - isPlaying = currentPage == 1, - speed = 1.2f // Faster for smoother perception - ) - if (currentPage == 1) { - LottieAnimation( - composition = lottieComp, - progress = { progress }, - modifier = Modifier - .fillMaxSize() - .graphicsLayer { - // Hardware acceleration for smooth rendering - compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen - }, - maintainOriginalImageBounds = true - ) - } - } + } + + // Idea animation (page 1) + androidx.compose.animation.AnimatedVisibility( + visible = currentPage == 1, + enter = fadeIn(animationSpec = tween(200)), + exit = fadeOut(animationSpec = tween(200)) + ) { + ideaComposition?.let { comp -> + val lottieComp = comp as? com.airbnb.lottie.LottieComposition + val progress by animateLottieCompositionAsState( + composition = lottieComp, + iterations = 1, + isPlaying = true, + speed = 1.5f + ) + LottieAnimation( + composition = lottieComp, + progress = { progress }, + modifier = Modifier.fillMaxSize(), + maintainOriginalImageBounds = true + ) } - - // Free page - money animation (page 2) - val shouldLoadMoney = currentPage in 1..3 - if (shouldLoadMoney) { - moneyComposition?.let { comp -> - val lottieComp = comp as? com.airbnb.lottie.LottieComposition - val progress by animateLottieCompositionAsState( - composition = lottieComp, - iterations = 1, - isPlaying = currentPage == 2, - speed = 1.2f - ) - if (currentPage == 2) { - LottieAnimation( - composition = lottieComp, - progress = { progress }, - modifier = Modifier - .fillMaxSize() - .graphicsLayer { - compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen - }, - maintainOriginalImageBounds = true - ) - } - } + } + + // Money animation (page 2) + androidx.compose.animation.AnimatedVisibility( + visible = currentPage == 2, + enter = fadeIn(animationSpec = tween(200)), + exit = fadeOut(animationSpec = tween(200)) + ) { + moneyComposition?.let { comp -> + val lottieComp = comp as? com.airbnb.lottie.LottieComposition + val progress by animateLottieCompositionAsState( + composition = lottieComp, + iterations = 1, + isPlaying = true, + speed = 1.5f + ) + LottieAnimation( + composition = lottieComp, + progress = { progress }, + modifier = Modifier.fillMaxSize(), + maintainOriginalImageBounds = true + ) } - - // Secure page - lock animation (page 3) - val shouldLoadLock = currentPage in 2..4 - if (shouldLoadLock) { - lockComposition?.let { comp -> - val lottieComp = comp as? com.airbnb.lottie.LottieComposition - val progress by animateLottieCompositionAsState( - composition = lottieComp, - iterations = 1, - isPlaying = currentPage == 3, - speed = 1.2f - ) - if (currentPage == 3) { - LottieAnimation( - composition = lottieComp, - progress = { progress }, - modifier = Modifier - .fillMaxSize() - .graphicsLayer { - compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen - }, - maintainOriginalImageBounds = true - ) - } - } + } + + // Lock animation (page 3) + androidx.compose.animation.AnimatedVisibility( + visible = currentPage == 3, + enter = fadeIn(animationSpec = tween(200)), + exit = fadeOut(animationSpec = tween(200)) + ) { + lockComposition?.let { comp -> + val lottieComp = comp as? com.airbnb.lottie.LottieComposition + val progress by animateLottieCompositionAsState( + composition = lottieComp, + iterations = 1, + isPlaying = true, + speed = 1.5f + ) + LottieAnimation( + composition = lottieComp, + progress = { progress }, + modifier = Modifier.fillMaxSize(), + maintainOriginalImageBounds = true + ) } - - // Private page - book animation (page 4) - val shouldLoadBook = currentPage in 3..4 - if (shouldLoadBook) { - bookComposition?.let { comp -> - val lottieComp = comp as? com.airbnb.lottie.LottieComposition - val progress by animateLottieCompositionAsState( - composition = lottieComp, - iterations = 1, - isPlaying = currentPage == 4, - speed = 1.2f - ) - if (currentPage == 4) { - LottieAnimation( - composition = lottieComp, - progress = { progress }, - modifier = Modifier - .fillMaxSize() - .graphicsLayer { - compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen - }, - maintainOriginalImageBounds = true - ) - } - } + } + + // Book animation (page 4) + androidx.compose.animation.AnimatedVisibility( + visible = currentPage == 4, + enter = fadeIn(animationSpec = tween(200)), + exit = fadeOut(animationSpec = tween(200)) + ) { + bookComposition?.let { comp -> + val lottieComp = comp as? com.airbnb.lottie.LottieComposition + val progress by animateLottieCompositionAsState( + composition = lottieComp, + iterations = 1, + isPlaying = true, + speed = 1.5f + ) + LottieAnimation( + composition = lottieComp, + progress = { progress }, + modifier = Modifier.fillMaxSize(), + maintainOriginalImageBounds = true + ) } } }