feat: Optimize onboarding animations and improve page swiping performance

This commit is contained in:
k1ngsterr1
2026-01-15 22:40:24 +05:00
parent e7e6d23631
commit 80f5e436ee

View File

@@ -247,15 +247,17 @@ fun OnboardingScreen(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(150.dp), .height(150.dp),
// Pre-load adjacent pages for smooth swiping
beyondBoundsPageCount = 2,
flingBehavior = PagerDefaults.flingBehavior( flingBehavior = PagerDefaults.flingBehavior(
state = pagerState, state = pagerState,
lowVelocityAnimationSpec = tween( lowVelocityAnimationSpec = tween(
durationMillis = 300, durationMillis = 250,
easing = FastOutSlowInEasing easing = FastOutSlowInEasing
), ),
snapAnimationSpec = spring( snapAnimationSpec = spring(
dampingRatio = Spring.DampingRatioLowBouncy, dampingRatio = Spring.DampingRatioNoBouncy,
stiffness = Spring.StiffnessLow stiffness = Spring.StiffnessMedium
) )
) )
) { page -> ) { page ->
@@ -372,42 +374,20 @@ fun AnimatedRosettaLogo(
bookComposition: Any?, bookComposition: Any?,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val currentPage = pagerState.currentPage // Use derivedStateOf for optimized reads
val pageOffset = pagerState.currentPageOffsetFraction val currentPage by remember { derivedStateOf { pagerState.currentPage } }
// 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"
)
Box( Box(
modifier = modifier modifier = modifier,
.graphicsLayer {
// Enable hardware acceleration
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
},
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
// Pre-render all animations to avoid lag // Rosetta icon (page 0) with pulse animation like splash screen
Box( androidx.compose.animation.AnimatedVisibility(
modifier = Modifier visible = currentPage == 0,
.fillMaxSize() enter = fadeIn(animationSpec = tween(200)),
.graphicsLayer { exit = fadeOut(animationSpec = tween(200))
// Hardware layer for better performance
renderEffect = null
},
contentAlignment = Alignment.Center
) { ) {
// Rosetta icon (page 0) with pulse animation like splash screen Box(contentAlignment = Alignment.Center) {
if (currentPage == 0) {
val pulseScale by rememberInfiniteTransition(label = "pulse").animateFloat( val pulseScale by rememberInfiniteTransition(label = "pulse").animateFloat(
initialValue = 1f, initialValue = 1f,
targetValue = 1.1f, targetValue = 1.1f,
@@ -418,7 +398,7 @@ fun AnimatedRosettaLogo(
label = "pulseScale" label = "pulseScale"
) )
// Glow effect behind logo - same style as splash screen // Glow effect behind logo
Box( Box(
modifier = Modifier modifier = Modifier
.size(180.dp) .size(180.dp)
@@ -429,7 +409,7 @@ fun AnimatedRosettaLogo(
) )
) )
// Main logo - circular like splash screen // Main logo
Image( Image(
painter = painterResource(id = R.drawable.rosetta_icon), painter = painterResource(id = R.drawable.rosetta_icon),
contentDescription = "Rosetta Logo", contentDescription = "Rosetta Logo",
@@ -438,111 +418,97 @@ fun AnimatedRosettaLogo(
.clip(CircleShape) .clip(CircleShape)
) )
} }
}
// Fast page - idea animation (page 1)
// Load adjacent pages for smooth swiping // Idea animation (page 1)
val shouldLoadIdea = currentPage in 0..2 androidx.compose.animation.AnimatedVisibility(
if (shouldLoadIdea) { visible = currentPage == 1,
ideaComposition?.let { comp -> enter = fadeIn(animationSpec = tween(200)),
val lottieComp = comp as? com.airbnb.lottie.LottieComposition exit = fadeOut(animationSpec = tween(200))
val progress by animateLottieCompositionAsState( ) {
composition = lottieComp, ideaComposition?.let { comp ->
iterations = 1, val lottieComp = comp as? com.airbnb.lottie.LottieComposition
isPlaying = currentPage == 1, val progress by animateLottieCompositionAsState(
speed = 1.2f // Faster for smoother perception composition = lottieComp,
) iterations = 1,
if (currentPage == 1) { isPlaying = true,
LottieAnimation( speed = 1.5f
composition = lottieComp, )
progress = { progress }, LottieAnimation(
modifier = Modifier composition = lottieComp,
.fillMaxSize() progress = { progress },
.graphicsLayer { modifier = Modifier.fillMaxSize(),
// Hardware acceleration for smooth rendering maintainOriginalImageBounds = true
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen )
},
maintainOriginalImageBounds = true
)
}
}
} }
}
// Free page - money animation (page 2)
val shouldLoadMoney = currentPage in 1..3 // Money animation (page 2)
if (shouldLoadMoney) { androidx.compose.animation.AnimatedVisibility(
moneyComposition?.let { comp -> visible = currentPage == 2,
val lottieComp = comp as? com.airbnb.lottie.LottieComposition enter = fadeIn(animationSpec = tween(200)),
val progress by animateLottieCompositionAsState( exit = fadeOut(animationSpec = tween(200))
composition = lottieComp, ) {
iterations = 1, moneyComposition?.let { comp ->
isPlaying = currentPage == 2, val lottieComp = comp as? com.airbnb.lottie.LottieComposition
speed = 1.2f val progress by animateLottieCompositionAsState(
) composition = lottieComp,
if (currentPage == 2) { iterations = 1,
LottieAnimation( isPlaying = true,
composition = lottieComp, speed = 1.5f
progress = { progress }, )
modifier = Modifier LottieAnimation(
.fillMaxSize() composition = lottieComp,
.graphicsLayer { progress = { progress },
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen modifier = Modifier.fillMaxSize(),
}, maintainOriginalImageBounds = true
maintainOriginalImageBounds = true )
)
}
}
} }
}
// Secure page - lock animation (page 3)
val shouldLoadLock = currentPage in 2..4 // Lock animation (page 3)
if (shouldLoadLock) { androidx.compose.animation.AnimatedVisibility(
lockComposition?.let { comp -> visible = currentPage == 3,
val lottieComp = comp as? com.airbnb.lottie.LottieComposition enter = fadeIn(animationSpec = tween(200)),
val progress by animateLottieCompositionAsState( exit = fadeOut(animationSpec = tween(200))
composition = lottieComp, ) {
iterations = 1, lockComposition?.let { comp ->
isPlaying = currentPage == 3, val lottieComp = comp as? com.airbnb.lottie.LottieComposition
speed = 1.2f val progress by animateLottieCompositionAsState(
) composition = lottieComp,
if (currentPage == 3) { iterations = 1,
LottieAnimation( isPlaying = true,
composition = lottieComp, speed = 1.5f
progress = { progress }, )
modifier = Modifier LottieAnimation(
.fillMaxSize() composition = lottieComp,
.graphicsLayer { progress = { progress },
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen modifier = Modifier.fillMaxSize(),
}, maintainOriginalImageBounds = true
maintainOriginalImageBounds = true )
)
}
}
} }
}
// Private page - book animation (page 4)
val shouldLoadBook = currentPage in 3..4 // Book animation (page 4)
if (shouldLoadBook) { androidx.compose.animation.AnimatedVisibility(
bookComposition?.let { comp -> visible = currentPage == 4,
val lottieComp = comp as? com.airbnb.lottie.LottieComposition enter = fadeIn(animationSpec = tween(200)),
val progress by animateLottieCompositionAsState( exit = fadeOut(animationSpec = tween(200))
composition = lottieComp, ) {
iterations = 1, bookComposition?.let { comp ->
isPlaying = currentPage == 4, val lottieComp = comp as? com.airbnb.lottie.LottieComposition
speed = 1.2f val progress by animateLottieCompositionAsState(
) composition = lottieComp,
if (currentPage == 4) { iterations = 1,
LottieAnimation( isPlaying = true,
composition = lottieComp, speed = 1.5f
progress = { progress }, )
modifier = Modifier LottieAnimation(
.fillMaxSize() composition = lottieComp,
.graphicsLayer { progress = { progress },
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen modifier = Modifier.fillMaxSize(),
}, maintainOriginalImageBounds = true
maintainOriginalImageBounds = true )
)
}
}
} }
} }
} }