Blog Infos
Author
Published
Topics
, , , ,
Published

In this tutorial, we will create an animated logo with a frosted glass effect. We will use the haze library by Chris Banes to simulate glass and graphicsLayer for 3D animation.

Project Setup

First, you need to add the haze dependency to your build.gradle:

repositories {
mavenCentral()
}
dependencies {
implementation("dev.chrisbanes.haze:haze:<version>")
}
Main Component

PerplexityLogo consists of two blocks (RoundedBoxLeft and RoundedBoxRight), which create the frosted glass effect.

@Composable
fun PerplexityLogo(
hazeStateLeft: HazeState,
hazeStateRight: HazeState,
modifier: Modifier
) {
val containerColor = MaterialTheme.colorScheme.surface
val hazeStyle = HazeStyle(
backgroundColor = containerColor,
tints = listOf(
HazeTint(containerColor.copy(alpha = if (containerColor.luminance() >= 0.5) 0.3f else 0.1f))
),
blurRadius = 10.dp,
noiseFactor = 0.3f
)
val infiniteTransition = rememberInfiniteTransition()
val animationProgress by infiniteTransition.animateFloat(
initialValue = 1f,
targetValue = 0f,
animationSpec = infiniteRepeatable(
animation = tween(5_000),
repeatMode = RepeatMode.Restart
)
)
Box(modifier = modifier) {
val pages = 8
MutableList(pages) { index ->
val rotationAngle = (animationProgress * 360 + (index * 360 / pages)) % 360
if (rotationAngle in 0f..180f) {
RoundedBoxLeft(rotationAngle - 90f, hazeStateLeft, hazeStyle, Modifier)
} else {
RoundedBoxRight(rotationAngle + 90f, hazeStateRight, hazeStyle, Modifier)
}
}
}
}
view raw Logo.kt hosted with ❤ by GitHub

💡 This approach was chosen because, after rotating the element by 180°, the image was mirrored, which didn’t produce the desired effect. In the image below, you can see that the yellow color appears on both the left and right sides.

It is also essential to specify hazeStyle. If not set, the app will crash with the following error:

java.lang.IllegalArgumentException: backgroundColor not specified. Please provide a color.

To avoid this, define your own style or use the predefined ones from the Materials library. In my example, I used a custom style to experiment with different types of glass effects.

val containerColor = MaterialTheme.colorScheme.surface
val lightAlpha = 0.3f
val darkAlpha = 0.1f
val hazeStyle = HazeStyle(
backgroundColor = containerColor,
tints = listOf(
HazeTint(
containerColor.copy(alpha = if (containerColor.luminance() >= 0.5) lightAlpha else darkAlpha),
)
),
blurRadius = 10.dp,
noiseFactor = 0.3f,
fallbackTint = HazeTint.Unspecified,
)
view raw Logo.kt hosted with ❤ by GitHub
RoundedBox

The key difference between the left and right parts is the transformOrigin value:

transformOrigin = TransformOrigin(1f, 0f)

Both blocks use graphicsLayer to create a perspective effect.

@Composable
fun RoundedBoxLeft(rotationAngle: Float, hazeState: HazeState, hazeStyle: HazeStyle, modifier: Modifier) {
Page(
hazeState = hazeState,
hazeStyle = hazeStyle,
borderColor = Color(0xFF24F4FE),
modifier = modifier.graphicsLayer {
rotationY = rotationAngle
rotationX = -45f
cameraDistance = 100f
transformOrigin = TransformOrigin(1f, 0f)
}
)
}
view raw Logo.kt hosted with ❤ by GitHub
Page

This part is straightforward — a Box with a border and our glass effect. If needed, you can add a fill color or a parallax effect to make the logo look even more impressive.

zIndex

In our example, zIndex plays a crucial role

Here’s what will happen if you don’t set zIndex.

For the left side, we define it as:

zIndex(rotationAngle)

For the right side, we use:

zIndex(360 - rotationAngle)

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Kobweb:Creating websites in Kotlin leveraging Compose HTML

Kobweb is a Kotlin web framework that aims to make web development enjoyable by building on top of Compose HTML and drawing inspiration from Jetpack Compose.
Watch Video

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kobweb

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author o ...

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kob ...

Jobs

hazeSource

To achieve the blur effect, you first need to get information from the image behind. That’s why we extracted the hazeState from PerplexityLogo externally, where our background is, and use hazeSource to retrieve it. It’s essential to set zIndex = 0f

Box(
contentAlignment = Alignment.Center
) {
val hazeStateLeft = remember { HazeState() }
val hazeStateRight = remember { HazeState() }
Box(
modifier = Modifier
.offset(y = (-30).dp)
.size(300.dp)
.hazeSource(hazeStateRight, zIndex = 0f)
.hazeSource(hazeStateLeft, zIndex = 0f)
.graphicsLayer {
rotationZ = animationProgress * 1080
}
.clip(CircleShape)
.paint(
painter = painterResource(id = R.drawable.orb_bg),
contentScale = ContentScale.Crop
)
)
PerplexityLogo(
hazeStateLeft,
hazeStateRight,
modifier = Modifier.size(200.dp)
)
}
view raw Logo.kt hosted with ❤ by GitHub

In PerplexityLogo, we set the zIndex based on the rotation:

 

.hazeSource(hazeStateLeft, rotationAngle + 1f)

 

following the same principle as zIndex, but with +1 (since the previous layer was 0)

The result:

This code creates an animated Perplexity logo with a frosted glass effect. By using HazeEffect and graphicsLayer, we achieve a beautiful visual effect with smooth animation

đź”— Link to the repository with the code:
https://github.com/rmnkhr/perplexity_ui_experiment
Follow me on LinkedIn and Twitter 👋
Thank you!

This article is previously published on proandroiddev.com.

Menu