{"id":3220,"date":"2023-05-18T10:44:05","date_gmt":"2023-05-18T17:44:05","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/surface-duo\/?p=3220"},"modified":"2023-05-18T10:44:05","modified_gmt":"2023-05-18T17:44:05","slug":"jetpack-compose-animation-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/surface-duo\/jetpack-compose-animation-2\/","title":{"rendered":"Blooming love for Compose animation"},"content":{"rendered":"<p>\n  Hello Jetpack Compose developers,\n<\/p>\n<p>\n  Today we\u2019ll be continuing our blog series on animations in Jetpack Compose! This content was inspired by <a href=\"https:\/\/www.droidcon.com\/2022\/08\/01\/composable-sheep-a-compose-animations-journey\/\">Nicole Terc\u2019s Composable Sheep talk<\/a> from droidcon NYC.\n<\/p>\n<h2>More basic animations<\/h2>\n<p>\n  We\u2019ll be continuing to use our <code>drawSunflower<\/code> method and <code>Sketch<\/code> composable to build animations in this post, so if you haven\u2019t already, check out <a href=\"https:\/\/devblogs.microsoft.com\/surface-duo\/jetpack-compose-animation-1\/\">last week\u2019s blog post<\/a> to see how we built those functions!\n<\/p>\n<p>\n  Last week, we took a look at implementing a basic size animation for our sunflower garden.\n<\/p>\n<p>\n  Today, we\u2019ll learn how to do animations with two other properties: angle and color.\n<\/p>\n<h3>Rotation animation<\/h3>\n<p>\n  To add some rotation to our garden, we\u2019ll first have to revisit our flower drawing code. This time, our animation state will need to be float that varies between <code>0f<\/code> and <code>360f<\/code>, to represent the angle offset. To incorporate the angle into our drawing code, we only need to make one simple change:\n<\/p>\n<pre>  fun DrawScope.drawSunflower(sizePct: Float = 1f, rotation: Float = 0f) {\r\n      <em>drawPetals<\/em>(sizePct, rotation)\r\n      <em>drawCenter<\/em>(sizePct)\r\n  }\r\n\r\n  \/\/ drawCenter remains the same\r\n\r\n  fun DrawScope.drawPetals(sizePct: Float, rotation: Float) {\r\n      val numPetals = 8\r\n      var angle = rotation\r\n      val size = <em>Size<\/em>(width = 30f, height = 75f) * sizePct\r\n\r\n      <em>repeat<\/em>(numPetals) <strong>{<br \/>        <\/strong><em>rotate<\/em>(angle) <strong>{<br \/>            <\/strong>drawOval(\r\n                  color = <em>Yellow<\/em>,\r\n                  topLeft = <em>Offset<\/em>(center.x - size.width \/ 2, center.y),\r\n                  size = size\r\n              )\r\n          <strong>}<br \/>        <\/strong>angle += 360 \/ numPetals\r\n      <strong>}<br \/><\/strong>\r\n  }<\/pre>\n<p>\n  Instead of starting the <code>angle<\/code> variable at <code>0f<\/code> when making calculations for where the petal ovals will be drawn, we simply start at the current angle offset in the rotation. The <code>rotation<\/code> angle only needs to be passed into the <code>drawPetal<\/code> function, so the <code>drawCenter<\/code> function can remain the same as before.\n<\/p>\n<p>\n  To set up the animation behavior, let\u2019s remind ourselves of what the <code>Sketch<\/code> composable and animation spec methods <a href=\"https:\/\/developer.android.com\/jetpack\/compose\/animation\/customize#infiniterepeatable\"><code>infiniteRepeatable<\/code><\/a> and <a href=\"https:\/\/developer.android.com\/jetpack\/compose\/animation\/customize#tween\"><code>tween<\/code><\/a> look like:\n<\/p>\n<pre>  @Composable\r\n  fun Sketch(\r\n      modifier: Modifier = Modifier,\r\n      targetValue: Float,\r\n      animationSpec: AnimationSpec&lt;Float&gt;,\r\n      onDraw: DrawScope.(Float) -&gt; Unit\r\n  )\r\n\r\n  @Stable\r\n  fun &lt;T&gt; infiniteRepeatable(\r\n      animation: DurationBasedAnimationSpec&lt;T&gt;,\r\n      repeatMode: RepeatMode = RepeatMode.<em>Restart<\/em>,\r\n      initialStartOffset: StartOffset = StartOffset(0)\r\n  ): InfiniteRepeatableSpec&lt;T&gt;\r\n\r\n  @Stable\r\n  fun &lt;T&gt; tween(\r\n      durationMillis: Int = <em>DefaultDurationMillis<\/em>,\r\n      delayMillis: Int = 0,\r\n      easing: Easing = <em>FastOutSlowInEasing<\/em>\r\n  ): TweenSpec&lt;T&gt;<\/pre>\n<p>\n  We already noted that our animation state should vary between <code>0f<\/code> and <code>360f<\/code> to cover every angle, so we\u2019ll set the <code>targetValue<\/code> to <code>360f<\/code>. \n<\/p>\n<p>\n  For the animation spec, I want my sunflowers to rotate at a constant speed infinitely. To make the rotation look seamless as it repeats, we\u2019ll leave the <code>repeatMode<\/code> and <code>delayMillis<\/code> parameters at their default values. And to achieve a constant rotation speed, this time we\u2019ll use a <a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/compose\/animation\/core\/package-summary#LinearEasing()\"><code>LinearEasing<\/code><\/a> so the angle changes at a constant rate.\n<\/p>\n<p>\n  Putting all that together, we can write the <code>AnimatedRotationSunflower<\/code> composable:\n<\/p>\n<pre>  @Composable\r\n  fun AnimatedRotationSunflower() {\r\n      <em>Sketch<\/em>(\r\n          modifier = Modifier.<em>size<\/em>(100.<em>dp<\/em>),\r\n          targetValue = 360f,\r\n          animationSpec = <em>infiniteRepeatable<\/em>(<em>tween<\/em>(durationMillis = 2000, easing = <em>LinearEasing<\/em>))\r\n      ) <strong>{ <\/strong>angle <strong>-&gt;\r\n        <\/strong><em>drawSunflower<\/em>(rotation = angle)\r\n      <strong>}<\/strong>\r\n  }<\/pre>\n<p>\n  Look at our garden now!\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"345\" height=\"155\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2023\/05\/word-image-3220-2.gif\" class=\"wp-image-3222\" \/>\n<\/p>\n<h3>Color animation<\/h3>\n<p>\n  As beautiful as the sunflowers are in yellow and brown, I think the garden could benefit from some additional colors! Let\u2019s try to add some color-based animations.\n<\/p>\n<p>\n  Thanks to the composable sheep talk, we know that <a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/compose\/ui\/graphics\/Color#hsv(kotlin.Float,kotlin.Float,kotlin.Float,kotlin.Float,androidx.compose.ui.graphics.colorspace.Rgb)\"><code>Color.hsv<\/code><\/a> method is a great way to animate color based on angle, since the <code>hue<\/code> parameter must be in the range of <code>0f-360f<\/code>.\n<\/p>\n<p>\n  To update our drawing code to accept our animated color, we only have to pass in the color parameter and use it when drawing our petal ovals:\n<\/p>\n<pre>  fun DrawScope.drawSunflower(sizePct: Float = 1f, rotation: Float = 0f, color: Color = <em>Yellow<\/em>) {\r\n      <em>drawPetals<\/em>(sizePct, rotation, color)\r\n      <em>drawCenter<\/em>(sizePct)\r\n  }\r\n\r\n  \/\/ drawCenter remains the same\r\n\r\n  fun DrawScope.drawPetals(sizePct: Float, rotation: Float, color: Color) {\r\n      val numPetals = 8\r\n      var angle = rotation\r\n      val size = <em>Size<\/em>(width = 30f, height = 75f) * sizePct\r\n\r\n      <em>repeat<\/em>(numPetals) <strong>{\r\n        <\/strong><em>rotate<\/em>(angle) <strong>{\r\n            <\/strong>drawOval(\r\n                  color = color,\r\n                  topLeft = <em>Offset<\/em>(center.x - size.width \/ 2, center.y),\r\n                  size = size\r\n              )\r\n          <strong>}\r\n        <\/strong>angle += 360 \/ numPetals\r\n      <strong>}<\/strong>\r\n  }<\/pre>\n<p>\n  Super simple! As for our animation, I want very similar behavior to our rotation animation from above \u2013 seamless infinite looping, constant rate of change \u2013 so our final composable will look like this:\n<\/p>\n<pre>  @Composable\r\n  fun AnimatedColorSunflower() {\r\n      <em>Sketch<\/em>(\r\n          modifier = Modifier.<em>size<\/em>(100.<em>dp<\/em>),\r\n          targetValue = 360f,\r\n          animationSpec = <em>infiniteRepeatable<\/em>(<em>tween<\/em>(durationMillis = 3000, easing = <em>LinearEasing<\/em>))\r\n      ) <strong>{ <\/strong>hue <strong>-&gt;\r\n        <\/strong><em>drawSunflower<\/em>(color = Color.hsv(hue = hue, saturation = 1f, value = 1f))\r\n      <strong>}<\/strong>\r\n  }<\/pre>\n<p>\n  As previously mentioned, the <code>Color.hsv<\/code> method accepts a hue between <code>0f<\/code> and <code>360f<\/code>, so we set our <code>targetValue<\/code> to <code>360f<\/code> and pass in the animated value to generate a new color.\n<\/p>\n<p>\n  Here\u2019s the result:\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"80\" height=\"80\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2023\/05\/word-image-3220-3.gif\" class=\"wp-image-3223\" srcset=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2023\/05\/word-image-3220-3.gif 80w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2023\/05\/word-image-3220-3-24x24.gif 24w, https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2023\/05\/word-image-3220-3-48x48.gif 48w\" sizes=\"(max-width: 80px) 100vw, 80px\" \/>\n<\/p>\n<p>\n  Pretty cool, but I think we can do even better! It would be awesome to have each individual petal change color over time for a sort of rainbow spinner effect. To do this, we\u2019ll mostly have to update the drawing code so that each petal is drawn with a slightly different color. We\u2019ll keep all the animation spec details the same, but instead of passing in one color for all the petals, we\u2019ll now just pass in the hue and do calculations while drawing:\n<\/p>\n<pre>  @Composable\r\n  fun AnimatedPetalColorSunflower() {\r\n      <em>Sketch<\/em>(\r\n          modifier = Modifier.<em>size<\/em>(100.<em>dp<\/em>),\r\n          targetValue = 360f,\r\n          animationSpec = <em>infiniteRepeatable<\/em>(<em>tween<\/em>(durationMillis = 3000, easing = <em>LinearEasing<\/em>))\r\n      ) <strong>{ <\/strong>hue <strong>-&gt;\r\n        <\/strong><em>drawSunflower<\/em>(hue = hue)\r\n      <strong>}<\/strong>\r\n  }\r\n\r\n  fun DrawScope.drawSunflower(sizePct: Float = 1f, rotation: Float = 0f, color: Color = <em>Yellow<\/em>, hue: Float? = null) {\r\n      <em>drawPetals<\/em>(sizePct, rotation, color, hue)\r\n      <em>drawCenter<\/em>(sizePct)\r\n  }\r\n\r\n  \/\/ drawCenter remains the same\r\n\r\n  fun DrawScope.drawPetals(sizePct: Float, rotation: Float, color: Color, hue: Float?) {\r\n      val numPetals = 8\r\n      var angle = rotation\r\n      val size = <em>Size<\/em>(width = 30f, height = 75f) * sizePct\r\n\r\n      <em>repeat<\/em>(numPetals) <strong>{ <\/strong>petal <strong>-&gt;\r\n        <\/strong><em>rotate<\/em>(angle) <strong>{\r\n            <\/strong>drawOval(\r\n                  color = hue?.<em>let <\/em><strong>{ <\/strong>Color.hsv(hue = (hue + petal * 10f).<em>mod<\/em>(360f), saturation = 1f, value = 1f) <strong>}\r\n                    <\/strong>?: color,\r\n                  topLeft = <em>Offset<\/em>(center.x - size.width \/ 2, center.y),\r\n                  size = size\r\n              )\r\n          <strong>}\r\n        <\/strong>angle += 360 \/ numPetals\r\n      <strong>}<\/strong>\r\n  }<\/pre>\n<p>\n  You can see that we now call <code>Color.hsv<\/code> in <code>drawPetals<\/code>, and we use the current petal number to offset the hue value. Because we are adding to the hue, we do have to call <code>mod(360f)<\/code> to make sure the final hue value is within the required range.\n<\/p>\n<p>\n  And here\u2019s the final result \u2013 look at how much cooler our garden is now:\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"345\" height=\"165\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2023\/05\/word-image-3220-4.gif\" class=\"wp-image-3224\" \/>\n<\/p>\n<p>\n  And that\u2019s it for the additional basic animations we\u2019ll cover today. The possibilities really are endless \u2013 you could also animate the number of petals, the position of the flower, or any combination of the properties we already animated. Next week, we\u2019ll finish up this blog series by finalizing our flower varieties and adding some green \ud83d\udfe9!\n<\/p>\n<h2>Resources and feedback<\/h2>\n<p>\n  The content covered today is part of <a href=\"https:\/\/github.com\/khalp\/animated-garden\/pull\/2\">PR #2<\/a> in the <a href=\"https:\/\/github.com\/khalp\/animated-garden\">animated-garden repo<\/a>. \n<\/p>\n<p>\n  To learn more about Jetpack Compose animations and creative coding, check out these resources:\n<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/nicole-terc\/composable-sheep\">nicole-terc\/composable-sheep (github.com)<\/a>\n  <\/li>\n<li><a href=\"https:\/\/github.com\/drinkthestars\/composable-sheep-sketches\">drinkthestars\/composable-sheep-sketches: Funky composable sheep \ud83d\ude0e \ud83d\udc0f (github.com)<\/a>\n  <\/li>\n<li><a href=\"https:\/\/www.droidcon.com\/2022\/08\/01\/composable-sheep-a-compose-animations-journey\/\">Composable Sheep &#8211; A Compose Animations Journey<\/a>\n  <\/li>\n<li><a href=\"https:\/\/www.droidcon.com\/2022\/11\/15\/composable-sheep-the-creative-coding-epilogue\/\">Composable Sheep &#8211; The Creative Coding Epilogue<\/a>\n  <\/li>\n<li><a href=\"https:\/\/developer.android.com\/jetpack\/compose\/graphics\/draw\/overview\">Graphics in Jetpack Compose<\/a>\n  <\/li>\n<li><a href=\"https:\/\/developer.android.com\/jetpack\/compose\/animation\">Animations in Jetpack Compose<\/a>\n  <\/li>\n<li><a href=\"https:\/\/developer.android.com\/jetpack\/compose\/animation\/customize\">Customize animations in Jetpack Compose<\/a>\n  <\/li>\n<\/ul>\n<p>\n  If you have any questions, use the <a href=\"http:\/\/aka.ms\/SurfaceDuoSDK-Feedback\">feedback forum<\/a> or message us on <a href=\"https:\/\/twitter.com\/surfaceduodev\">Twitter @surfaceduodev<\/a>.\n<\/p>\n<p>\n  We won\u2019t be livestreaming this week, but you can check out the <a href=\"https:\/\/youtube.com\/c\/surfaceduodev\">archives on YouTube<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello Jetpack Compose developers, Today we\u2019ll be continuing our blog series on animations in Jetpack Compose! This content was inspired by Nicole Terc\u2019s Composable Sheep talk from droidcon NYC. More basic animations We\u2019ll be continuing to use our drawSunflower method and Sketch composable to build animations in this post, so if you haven\u2019t already, check [&hellip;]<\/p>\n","protected":false},"author":72597,"featured_media":3221,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[736,719,692],"class_list":["post-3220","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-surface-duo-sdk","tag-animation","tag-droidcon","tag-jetpack-compose"],"acf":[],"blog_post_summary":"<p>Hello Jetpack Compose developers, Today we\u2019ll be continuing our blog series on animations in Jetpack Compose! This content was inspired by Nicole Terc\u2019s Composable Sheep talk from droidcon NYC. More basic animations We\u2019ll be continuing to use our drawSunflower method and Sketch composable to build animations in this post, so if you haven\u2019t already, check [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/3220","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/users\/72597"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/comments?post=3220"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/3220\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media\/3221"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media?parent=3220"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/categories?post=3220"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/tags?post=3220"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}