How to Create an Atomic Loader in Jetpack Compose

Kappdev
4 min readSep 2, 2024

--

Welcome ๐Ÿ‘‹

In this article, we will create a stunning 3D Atomic Loader animation in Jetpack Compose.

Excited? ๐Ÿคฉ Letโ€™s dive in! โš›๏ธ๐Ÿš€

Created by Kappdev

Inspiration โœจ

This loader animation is inspired by the original work created by Martin van Driel using HTML and CSS, which is licensed under the MIT License.

I recreated this effect using Jetpack Compose to bring it to Android. Enjoy the tutorial!

Creating the Rotating Circle

To start, we need to create a RotatingCircle composable function. This function will render a single circle, which weโ€™ll use to compose the loader.

@Composable
fun RotatingCircle(
modifier: Modifier,
rotationX: Float,
rotationY: Float,
rotationZ: Float,
borderColor: Color,
borderWidth: Dp
) {
// Further implementation
}

Drawing the Circle

To achieve the desired effect, we subtract one circle from another and draw the result on a Canvas:

Canvas(modifier) {
// Define the path for the main circle
val mainCircle = Path().apply {
addOval(Rect(size.center, size.minDimension / 2))
}

// Adjust the position of the clipping circle to the left by borderWidth
val clipCenter = Offset(size.width / 2f - borderWidth.toPx(), size.height / 2f)
// Define the path for the clipping circle
val clipCircle = Path().apply {
addOval(Rect(clipCenter, size.minDimension / 2))
}

// Subtract the clipping circle from the main circle
val path = Path().apply {
op(mainCircle, clipCircle, PathOperation.Difference)
}

// Draw the subtracted path
drawPath(path, borderColor)
}
Subtraction

Transformation

Next, we use graphicsLayer to rotate the Canvas in a 3-dimensional space:

Canvas(
modifier.graphicsLayer {
this.rotationX = rotationX
this.rotationY = rotationY
this.rotationZ = rotationZ
// To create a depth effect adjust the cameraDistance
cameraDistance = 12f * density
}
) {
// Drawing logic
}

Composing the Loader

To render the loader, we define theAtomicLoader composable function:

@Composable
fun AtomicLoader(
modifier: Modifier,
color: Color = Color.White,
borderWidth: Dp = 3.dp,
cycleDuration: Int = 1000
)

Parameters

โš›๏ธ modifier ๐Ÿ‘‰ Modifier to be applied to the loader container.

โš›๏ธ color ๐Ÿ‘‰ Color of the loader.

โš›๏ธ borderWidth ๐Ÿ‘‰ Width of the loaderโ€™s border.

โš›๏ธ cycleDuration ๐Ÿ‘‰ Duration of one complete rotation cycle in milliseconds.

Creating the Rotation Animation

To animate the loader, we define an infinite rotation that cycles from 0 to 360 degrees:

val infiniteTransition = rememberInfiniteTransition("InfiniteAtomicLoaderTransition")

val rotation by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 360f,
animationSpec = infiniteRepeatable(
animation = tween(cycleDuration, easing = LinearEasing)
),
label = "AtomicLoaderRotation"
)

Placing the Circles

Finally, we position the circles inside the Box composable and apply the rotation animation:

Box(modifier) {
RotatingCircle(
modifier = Modifier.matchParentSize(),
rotationX = 35f,
rotationY = -45f,
rotationZ = -90f + rotation,
borderColor = color,
borderWidth = borderWidth
)
RotatingCircle(
modifier = Modifier.matchParentSize(),
rotationX = 50f,
rotationY = 10f,
rotationZ = rotation,
borderColor = color,
borderWidth = borderWidth
)
RotatingCircle(
modifier = Modifier.matchParentSize(),
rotationX = 35f,
rotationY = 55f,
rotationZ = 90f + rotation,
borderColor = color,
borderWidth = borderWidth
)
}

Congratulations ๐Ÿฅณ! Weโ€™ve successfully built it ๐Ÿ‘. You can find the full code on GitHub Gist ๐Ÿง‘โ€๐Ÿ’ป. Letโ€™s explore the usage ๐Ÿ‘‡

Practical Usage ๐Ÿ’โ€โ™‚๏ธ

Letโ€™s create a Box with a radial gradient background and place the loader at the center to see the result.

โš ๏ธ Ensure the loader has a clearly defined size! If not, it might not appear or could cause unexpected behavior.

Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.radialGradient(
listOf(Color(0xFF3C4B57), Color(0xFF1C262B))
)
),
contentAlignment = Alignment.Center
) {
AtomicLoader(
Modifier.size(100.dp)
)
}

Output ๐Ÿ˜

Custom Loaders | Jetpack Compose

7 stories

Thank you for reading this article! โค๏ธ If you found it enjoyable and valuable, show your appreciation by clapping ๐Ÿ‘ and following Kappdev for more exciting articles ๐Ÿ˜Š

--

--

Kappdev
Kappdev

Written by Kappdev

๐Ÿ’ก Curious Explorer ๐Ÿงญ Kotlin and Compose enthusiast ๐Ÿ‘จโ€๐Ÿ’ป Passionate about self-development and growth โค๏ธโ€๐Ÿ”ฅ Push your boundaries ๐Ÿš€

No responses yet