{"id":215,"date":"2018-06-09T22:06:43","date_gmt":"2018-06-09T22:06:43","guid":{"rendered":"http:\/\/www.bullcrane.com\/angular\/?p=215"},"modified":"2025-12-20T17:19:18","modified_gmt":"2025-12-20T17:19:18","slug":"animation","status":"publish","type":"post","link":"http:\/\/www.bullcrane.com\/angular\/2018\/06\/09\/animation\/","title":{"rendered":"Animation"},"content":{"rendered":"<style>\n.mad_hc { background-color:red; }<br \/>\n.friendly_hc { background-color:green; }<br \/>\n.mad { transition-duration: 3s; background-color:red; }<br \/>\n.friendly { transition-duration: 3s; background-color:green; }<br \/>\n<\/style>\n<p>In web app development, <em>animation<\/em> is automation of the position and display characteristics of DOM elements.&nbsp; Angular provides its own programming interface for animations, insulating the developer from browser compatibility concerns.&nbsp; The developer defines styles for different states a DOM element can take on, and defines transitions for how to change from one state to another.&nbsp; Let&#8217;s first look at a non-animated example, and then animate it.<\/p>\n<h6>Hard cut<\/h6>\n<p>Say we have component with a variable named <em>mood<\/em>.&nbsp; Sometimes it holds the value &#8220;friendly&#8221; and sometimes it holds the value &#8220;mad.&#8221;&nbsp; In the component&#8217;s html template we display the value, but additionally we want a red background if mad.&nbsp; So far none of this requires Angular animation.&nbsp; We can use stock Angular to set the background color based on the mood value.<\/p>\n<p>&lt;p [style.background-color]=&#8221;mood==&#8217;mad&#8217;?&#8217;red&#8217;:&#8217;green'&#8221;&gt;{{mood}}&lt;\/p&gt;<\/p>\n<p id=\"moody_hc\" class=\"mad_hc\">mad<\/p>\n<p><input type=\"button\" onclick=\"var ele=document.getElementById('moody_hc');if (ele.innerHTML=='mad') {ele.innerHTML='friendly';ele.className='friendly_hc';} else {ele.innerHTML='mad';ele.className='mad_hc';}\" value=\"toggle mood\"><\/p>\n<p>This creates what <a href=\"http:\/\/rachelnabors.com\/\">Rachel Nabors<\/a> calls a <em>hard cut<\/em>.&nbsp; This term comes from video\/film editing and means there is no transition from one scene to the next.&nbsp; The first scene ends and the next scene appears abruptly.&nbsp; In our case, the moment the value for mood changes, the background color instantly changes.&nbsp; The text does too, but for simplicity let&#8217;s just focus our work on the background color.<\/p>\n<h6>Crossfade<\/h6>\n<p>We can introduce Angular animation to create a transition instead of a hard cut.&nbsp; Say we want the colors to slowly transition for three seconds when the mood changes state, a rudimentary animation known in video\/film editing as a <em>crossfade<\/em>.<\/p>\n<p>Instead of directly setting the style for mad, we define an animation trigger.&nbsp; In the trigger we define the style for mad (red) and the style for friendly (green), and how to animate the transition&nbsp;(take a leisurely 3 seconds to change color instead of doing it instantly).&nbsp; I have named the animation trigger <em>moodAni.<\/em><\/p>\n<p>&lt;p [@moodAni]=&#8217;mood&#8217;&gt;{{mood}}&lt;\/p&gt;<\/p>\n<p id=\"moody\" class=\"mad\">mad<\/p>\n<p><input type=\"button\" onclick=\"var ele=document.getElementById('moody');if (ele.innerHTML=='mad') {ele.innerHTML='friendly';ele.className='friendly';} else {ele.innerHTML='mad';ele.className='mad';}\" value=\"toggle mood\"><\/p>\n<p>In Angular Typescript, in addition to the usual @Component <em>selector<\/em> and <em>template, <\/em>we define <em>animations<\/em>.&nbsp; The &lt;=&gt; means do the transition when going either way, from mad to friendly or from friendly to mad:\n<\/p>\n<pre>@Component({\n  selector: 'app-root',\n  template: '&lt;p [@moodAni]=\"mood\"&gt;{{mood}}&lt;\/p&gt;',\n  animations: [ trigger('moodAni',\n    [ state('mad',style({backgroundColor:'red'})),\n      state('friendly',style({backgroundColor:'green'})),\n      transition('friendly &lt;=&gt; mad',animate('3s')) ]\n    ) ]\n  })<\/pre>\n<h6>Crossfade?<\/h6>\n<p>The term &#8220;crossfade&#8221; isn&#8217;t in the code.&nbsp; Any transition for an element that isn&#8217;t changing size or position is a crossfade.&nbsp; More generally, what the Angular animation module does is known as <a href=\"https:\/\/en.wikipedia.org\/wiki\/Inbetweening\">tweening<\/a>.&nbsp;&nbsp;We define states or keyframes and then specify how long to take between each.&nbsp; This is quite a bit like CSS transitions.&nbsp; Indeed, you might be wondering how I managed to get Angular examples working directly inside this blog.&nbsp; I didn&#8217;t.&nbsp; They are pure CSS equivalents.<\/p>\n<h6>Nice corral you got there, Angular<\/h6>\n<p>If you haven&#8217;t made the plunge yet into Angular animations, you might be wondering if you&#8217;d be better off sticking with <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_Transitions\/Using_CSS_transitions\">CSS transitions<\/a>.&nbsp; It is possible.&nbsp; It&#8217;s what I was doing before I discovered Angular animations.&nbsp; I like the Angular way better.&nbsp; I don&#8217;t have to worry about browser differences, and the coding for animation is corralled into the component&#8217;s <em>animations<\/em> specification.<\/p>\n<h6>But some horses can get through that fence<\/h6>\n<p>Notice in the crossfade example above that if you click the button a second time quickly, the new animation immediately takes over.&nbsp; In Shufflizer I have a case where this isn&#8217;t desirable &#8212; the download progress spinner.&nbsp; For small playlists sometimes it would not fade smoothly in and out.&nbsp; When the download was done before the fade-in was done, the progress spinner would cut over to the fade-out animation.&nbsp; Nothing was technically wrong, but it was strange and unpolished.&nbsp; Here is how I solved it.<\/p>\n<p>Angular provides an event that fires at the beginning and the end of an animation.&nbsp; I use this event to prevent shutting the progress spinner while the fade-in animation is still running.<\/p>\n<pre>&lt;div class=\"dimmed\"\n  style=\"text-align:center;padding-top:100px;padding-left:20%;padding-right:20%\" \n  *ngIf=\"<span style=\"color: #ff0000;\">dimSet()<\/span>\" \n  [@dimAnim] \n  (@dimAnim.start)=\"<span style=\"color: #ff0000;\">setAnim($event)<\/span>\" \n  (@dimAnim.done)=\"<span style=\"color: #ff0000;\">setAnim($event)<\/span>\"&gt;\n  &lt;mat-card class=\"webLandscape\"&gt;\n    &lt;mat-card-content&gt;\n      &lt;h2 class=\"example-h2\"&gt;{{plLoadingDirection()}}loading...   {{progressPcnt()}}%&lt;\/h2&gt;\n      &lt;mat-progress-spinner mode=\"determinate\" [value]=\"progressPcnt()\" style=\"margin-left:auto;margin-right:auto\"&gt;\n      &lt;\/mat-progress-spinner&gt;\n    &lt;\/mat-card-content&gt;\n  &lt;\/mat-card&gt;\n&lt;\/div&gt;\n<\/pre>\n<p>The progress spinner renders when dimSet() is true.&nbsp; setAnim simply stashes the event object into a variable so dimSet can use it.<\/p>\n<p>It all comes down to the last line in dimSet.&nbsp; Return true if a playlist is loading <em>or if the fade-in animation is still running<\/em>.<\/p>\n<pre>setAnim(ev): void {\n  this.anim=ev;\n  }\n\ndimSet(): boolean {\n  let animStillRunning=this.anim \n    &amp;&amp; this.anim.triggerName == 'dimAnim' \n    &amp;&amp; this.anim.totalTime &gt; 1 \n    &amp;&amp; this.anim.phaseName == 'start' \n    &amp;&amp; this.anim.fromState == 'void' ;\n  return this.playlistService.plIsLoading \n    <span style=\"color: #ff0000;\">|| animStillRunning;<\/span>\n  }\n<\/pre>\n<p>I found the names of the event attributes on the <a href=\"https:\/\/angular.io\/api\/animations\/AnimationEvent\">AnimationEvent documentation<\/a> page.<\/p>\n<p>So I like the <em>animations<\/em> &#8220;corral&#8221; but when we need to know the status of an animation, it&#8217;s not an animation we can just set into motion and forget about, then special coding for animations comes back into our TypeScript code.<\/p>\n<p>For more information about Angular animation, see the <a href=\"https:\/\/angular.io\/guide\/animations\">Angular animations<\/a> page.  It&#8217;s where I learned most of what I know about Angular animations.<\/p>\n\n\n<h2 class=\"wp-block-heading\">December 2025<\/h2>\n\n\n\n<p>Several years have passed and now the Angular Animations module is deprecated.  Angular developers are to use CSS animations which are mature and well supported.  I have refactored Shufflizer, removing its dependency on the Angular Animations module.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In web app development, animation is automation of the position and display characteristics of DOM elements.&nbsp; Angular provides its own programming interface for animations, insulating the developer from browser compatibility concerns.&nbsp; The developer defines styles for different states a DOM element can take on, and defines transitions for how to change from one state to &hellip; <\/p>\n<p class=\"link-more\"><a href=\"http:\/\/www.bullcrane.com\/angular\/2018\/06\/09\/animation\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Animation&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[7],"tags":[],"_links":{"self":[{"href":"http:\/\/www.bullcrane.com\/angular\/wp-json\/wp\/v2\/posts\/215"}],"collection":[{"href":"http:\/\/www.bullcrane.com\/angular\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.bullcrane.com\/angular\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.bullcrane.com\/angular\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.bullcrane.com\/angular\/wp-json\/wp\/v2\/comments?post=215"}],"version-history":[{"count":55,"href":"http:\/\/www.bullcrane.com\/angular\/wp-json\/wp\/v2\/posts\/215\/revisions"}],"predecessor-version":[{"id":483,"href":"http:\/\/www.bullcrane.com\/angular\/wp-json\/wp\/v2\/posts\/215\/revisions\/483"}],"wp:attachment":[{"href":"http:\/\/www.bullcrane.com\/angular\/wp-json\/wp\/v2\/media?parent=215"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.bullcrane.com\/angular\/wp-json\/wp\/v2\/categories?post=215"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.bullcrane.com\/angular\/wp-json\/wp\/v2\/tags?post=215"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}