How to Design Animated Emojis in Jetpack Compose

Master the techniques for designing animated emojis in Jetpack Compose.
May 30 2022 · 7 min read

Background

We all use emojis in our day-to-day life. Using canvas in Jetpack compose, we can draw various cool emojis!

Today we are going to implement 6 emojis in Jetpack compose. We will make use compose canvas to draw them.

At the end of this article, you will have a basic idea of how you can implement emojis shown below in your applications.

Cool Emojis

We are what we repeatedly do. Excellence, then, is not an act, but a habit. Try out Justly and start building your habits today!

Let's get started with emoji!

1. Confused

To draw a confused emoji, we have used drawArc(), drawCircle() and drawline() methods of canvas.

  Canvas(
        modifier = Modifier
            .padding(top = 16.dp)
            .size(100.dp)
    ) {
        drawArc(
            color = Color(0xFFFFd301),
            startAngle = 0f,
            sweepAngle = 360f,
            useCenter = true
        )

        drawCircle(
            color = Color.White,
            center = Offset(x = 35.dp.toPx(), y = 30.dp.toPx()),
            radius = 30f,
        )

        drawCircle(
            color = Color.Black,
            center = Offset(-offsetX + 35.dp.toPx(), 30.dp.toPx()),
            radius = 10f,
        )

        drawCircle(
            color = Color.White,
            center = Offset(x = 65.dp.toPx(), y = 30.dp.toPx()),
            radius = 30f,
        )

        drawCircle(
            color = Color.Black,
            center = Offset(-offsetX + 65.dp.toPx(), 30.dp.toPx()),
            radius = 10f,
        )

        drawLine(
            start = Offset(x = 30.dp.toPx(), y = 70.dp.toPx()),
            end = Offset(x = 70.dp.toPx(), y = 70.dp.toPx()),
            color = Color.Black,
            strokeWidth = 10.0f
        )
    }

Here,

  1. drawArc() is used to draw emoji circles.
  2. drawCircle() methods at various points are used to draw eyes
  3. drawLine() at the end is used to show confused facial expressions.

With this code snippet we will be able to draw our Confused emoji, but without eye movements (You can observe eye movement in mentioned gif).

Now, to move eyes on given offset values we can have something like —


 val offsetX by animateValues(
        values = listOf(0f, 15f, -15f, 0f),
        animationSpec = infiniteRepeatable(
            animation = tween(durationMillis = 1500, easing = easing),
            repeatMode = RepeatMode.Restart
        )
    )

Here, our eyes will move on offsetX in the range of (0f,15f,-15f,0f) means 0f to -15f, then -15f to 15f and lastly from 15f to 0f as we have used -offsetX in the above code.

We can observe that animateValues, which is a composable, will help us change the values based on target positions.

A typical animateValues composable will look something like this. Just to note, we have already used this in our Jetpack compose progress animation article.


@Composable
fun animateValues(
    values: List<Float>,
    animationSpec: AnimationSpec<Float> = spring(),
): State<Float> {

    // 1. Create the groups zipping with next entry
    val groups by rememberUpdatedState(newValue = values.zipWithNext())
    // 2. Start the state with the first value
    val state = remember { mutableStateOf(values.first()) }

    LaunchedEffect(key1 = groups) {
        val (_, setValue) = state
        // Start the animation from 0 to groups quantity
        animate(
            initialValue = 0f,
            targetValue = groups.size.toFloat(),
            animationSpec = animationSpec,
        ) { frame, _ ->
            // Get which group is being evaluated
            val integerPart = frame.toInt()
            val (initialValue, finalValue) = groups[frame.toInt()]
            // Get the current "position" from the group animation
            val decimalPart = frame - integerPart
            // Calculate the progress between the initial and final value
            setValue(
                initialValue + (finalValue - initialValue) * decimalPart
            )
        }
    }
    return state
}

The complete composable for confused emoji is available on Github.

2. Happy

To draw a happy emoji, we have used drawArc(), drawCircle() and drawPath() methods of canvas in Jetpack compose.


  drawArc(
            color = Color(0xFFFFd301),
            startAngle = 0f,
            sweepAngle = 360f,
            useCenter = true
        )

        drawCircle(
            color = Color.White,
            center = Offset(x = 35.dp.toPx(), y = 30.dp.toPx()),
            radius = 30f,
        )
        
            val mouthPath = Path().let {
            it.moveTo(size * 0.22.dp.toPx(), size * 0.70.dp.toPx())
            it.quadraticBezierTo(
                size * 0.50.dp.toPx(), size * 0.80.dp.toPx(),
                size * 0.78.dp.toPx(), size * 0.70.dp.toPx()
            )
            it.quadraticBezierTo(
                size * 0.50.dp.toPx(), size * 0.95.dp.toPx(),
                size * 0.22.dp.toPx(), size * 0.70.dp.toPx()
            )
            it.close()
            it
        }
        drawPath(path = mouthPath, color = Color.Black)

Here,

  1. drawArc() is used to draw emoji circles
  2. drawCircle() methods at various points are used to draw eyes
  3. and drawPath() at the end is used to show happy facial expressions.

To draw a happy facial path:

  1. Set the starting point of the path to (x0, y0) by using the moveTo() method, where x0 is 22% of the path Size(250) and y0 is 70% of the pathSize.
  2. Draw a curved path from starting position(x0, y0) through (x1,y1) and end the path at (x2,y2), where x1 is 50% of the pathSize and y1 is 80% of the path size, x2 is 78% of the path size and y2 is 70% of the path size.
  3. Again we need to return to complete the happy mouth face. So, draw a curved path from (x2,y2) through(x3,y3) and end path at (x0,y0), where x3 is 50% of the path size, y3 is 95% of the path size, (x0,y0) are same as 22% and 70% of the path size.

The complete composable for happy emoji is available on Github.

3. Cool

To draw a cool emoji, we have again used drawArc(), drawCircle() and drawPath() methods of canvas in Jetpack compose.


 drawArc(
            color = Color(0xFFFFd301),
            startAngle = 0f,
            sweepAngle = 360f,
            useCenter = true
        )

        drawCircle(
            color = Color.White,
            center = Offset(x = 65.dp.toPx(), y = 30.dp.toPx()),
            radius = 20f,
        )
  
 val mouthPath = Path().let {
            it.moveTo(size * 0.20.dp.toPx(), size * 0.70.dp.toPx())
            it.quadraticBezierTo(
                size * 0.50.dp.toPx(), size * 0.80.dp.toPx(),
                size * 0.78.dp.toPx(), size * 0.70.dp.toPx()
            )
            it.quadraticBezierTo(
                size * 0.50.dp.toPx(), size * 1.60.dp.toPx(),
                size * 0.20.dp.toPx(), size * 0.70.dp.toPx()
            )
            it.close()
            it
        }
        drawPath(path = mouthPath, color = TongueColor)

Here,

  1. drawArc() is used to draw emoji circle.
  2. drawCircle() methods at various points are used to draw eyes
  3. and drawPath() at the end is used to show tongue expressions.

Here, we have kept the right eye of our emoji a bit smaller intentionally. Follow the happy emoji mouth path steps to draw a tongue of emoji.

The complete composable for cool emoji is available on Github.

4. Exclamation

To draw an exclamation emoji, we have once again used drawArc(), drawCircle() methods of canvas in Jetpack compose.


 Canvas(
        modifier = Modifier
            .padding(top = 40.dp)
            .size(100.dp)
    ) {
        drawArc(
            color = Color(0xFFFFd301),
            startAngle = 0f,
            sweepAngle = 360f,
            useCenter = true
        )
        drawCircle(
            color = Color.White,
            center = Offset(x = 35.dp.toPx(), y = 30.dp.toPx()),
            radius = eyeAnimation,
        )

        drawCircle(
            color = Color.Black,
            center = Offset(x = 35.dp.toPx(), y = 30.dp.toPx()),
            radius = 10f,
        )

        drawCircle(
            color = Color.White,
            center = Offset(x = 65.dp.toPx(), y = 30.dp.toPx()),
            radius = eyeAnimation,
        )

        drawCircle(
            color = Color.Black,
            center = Offset(x = 65.dp.toPx(), y = 30.dp.toPx()),
            radius = 10f,
        )

        drawCircle(
            color = ExclamationEmojiColor,
            center = Offset(x = 50.dp.toPx(), y = 70.dp.toPx()),
            radius = 20f,
        )
    }

Here,

  1. drawArc() is used to draw emoji circle.
  2. drawCircle() methods at various points are used to draw eyes

With this code snippet, we will be able to draw our Exclamation emoji, but without eye animation.

Now, to zoom eyes we can have something like:


 val infiniteTransition = rememberInfiniteTransition()

Here, we have used infiniteTransition and we remember it to save it in compose memory across composables.

We can use animateFloat to create the animation of type float that runs infinitely as a part of infiniteTransition.

Once the animation is created it will run from the initial value (1f) to the target value (30f) and will repeat forever.


   val eyeAnimation by infiniteTransition.animateFloat(
        initialValue = 1f,
        targetValue = 30f,
        animationSpec = infiniteRepeatable(
            animation = tween(1500, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        )
    )

Here, we have used tween as a duration-based AnimationSpec and it uses easing to adjust the animation’s fraction.

Easing allows the animating value to speed up and slow down, rather than moving at a constant speed.

Tween also has another parameter called durationMills which is the waiting period after each iteration of our animation(we have used 1500 in the Twin animation example).

That’s it for zooming emoji’s eyes.

The complete composable for exclamation emoji is available on Github.

5. Long tongue

To draw a long tongue emoji we have used drawArc(), drawOval() and drawPath() methods of canvas in Jetpack compose.


        drawArc(
            color = Color(0xFFFFd301),
            startAngle = 0f,
            sweepAngle = 360f,
            useCenter = true
        )

        drawOval(
            color = EmojiEyeColor,
            topLeft = Offset(x = 65.dp.toPx(), y = 30.dp.toPx()),
            size = Size(20f, 35f)
        )

          val mouthPath = Path().let {
            it.moveTo(size * 0.20.dp.toPx(), size * 0.70.dp.toPx())
            it.quadraticBezierTo(
                size * 0.50.dp.toPx(), size * 0.80.dp.toPx(),
                size * 0.78.dp.toPx(), size * 0.70.dp.toPx()
            )
            it.quadraticBezierTo(
                size * 0.50.dp.toPx(), size * 1.90.dp.toPx(),
                size * 0.20.dp.toPx(), size * 0.70.dp.toPx()
            )
            it.close()
            it
        }
        drawPath(path = mouthPath, color = TongueColor)

Here,

  1. drawArc() is used to draw emoji circle.
  2. drawOval() methods at various points are used to draw eyes
  3. and drawPath() at the end is used to show long tongue expressions.

To draw a long tongue of emoji, you can follow happy emoji mouth path steps.

The complete composable for long tongue emoji is available on Github.

6. Sad

To draw a sad emoji we have used drawArc(), drawOval() and drawPath() methods of canvas in Jetpack compose.


        drawArc(
            color = Color(0xFFFFd301),
            startAngle = 0f,
            sweepAngle = 360f,
            useCenter = true
        )

        drawOval(
            color = EmojiEyeColor,
            topLeft = Offset(x = 65.dp.toPx(), y = 30.dp.toPx()),
            size = Size(20f, 35f)
        )

       val mouthPath = Path().let {
            it.moveTo(size * 0.30.dp.toPx(), size * 0.70.dp.toPx())
            it.quadraticBezierTo(
                size * 0.45.dp.toPx(), size * 0.45.dp.toPx(),
                size * 0.65.dp.toPx(), size * 0.65.dp.toPx()
            )
            it.quadraticBezierTo(
                size * 0.45.dp.toPx(), size * 0.35.dp.toPx(),
                size * 0.30.dp.toPx(), size * 0.70.dp.toPx()
            )
            it.close()
            it
        }
        drawPath(path = mouthPath, color = TongueColor)

Here:-

  1. drawArc() is used to draw emoji circle
  2. drawOval() methods at various points are used to draw eyes and
  3. drawPath() at the end is used to show sad expressions.

To draw a sad emoji mouth expressions, you can follow happy emoji mouth path steps.

The complete composable for sad emoji is available on Github.

To Conclude

That’s it for all the Emojis, hope you learned something new!

You will have a basic idea of how Canvas works and how to make use of its basic functions to draw various shapes. You will also have an idea about how to animate shapes on Canvas using various animation functions.

Don’t worry if you didn’t get all the emojis. You can find the complete source code of all the above animations on Github.

As always, feedback and suggestions are welcome. Feel free to add them in the response section.


jimmy image
Jimmy Sanghani
Jimmy Sanghani is a tech nomad and cofounder at Canopas helping businesses use new age leverage - Code and Media - to grow their revenue exponentially. With over a decade of experience in both web and mobile app development, he has helped 100+ clients transform their visions into impactful digital solutions. With his team, he's helping clients navigate the digital landscape and achieve their objectives, one successful project at a time.


jimmy image
Jimmy Sanghani
Jimmy Sanghani is a tech nomad and cofounder at Canopas helping businesses use new age leverage - Code and Media - to grow their revenue exponentially. With over a decade of experience in both web and mobile app development, he has helped 100+ clients transform their visions into impactful digital solutions. With his team, he's helping clients navigate the digital landscape and achieve their objectives, one successful project at a time.

contact-footer
Say Hello!
footer
Subscribe Here!
Follow us on
2024 Canopas Software LLP. All rights reserved.