Auto Animate

November 10, 2023

AutoAnimate is a fantastic little javascript utility I just stumbled across on Twitter. To quote the AutoAnimate website, "AutoAnimate adds automatic animations to your JavaScript applications with a single line of code.". That's it, super easy to install and super easy to use.

I'm a big fan of simple little micro animations and embellishments to spice up websites so I decided to tinker with it using AlpineJs (another favourite of mine).  After seeing Ian's tweet I knew this was something I needed to checkout. 

Autoanimate tweet

Before

I threw together a simple snack generator that randomly grabs a snack and displays it in a grid. Nothing very exciting and as you can see in the video below, pretty blah when items are added/remove and shuffled. The important thing is that it add/removes DOM elements in a parent which is all AutoAnimate needs to see to make some magic happen. Here's the AlpineJs data class I used


Alpine.data("snacks", () => {
   return {
      
      emojis: [
         {name: 'Hamburger', emoji: '🍔'},
         {name: 'Pizza', emoji: '🍕'},
         {name: 'Taco', emoji: '🌮'},
         {name: 'Salad', emoji: '🥗'},
         {name: 'Sandwich', emoji: '🥪'},
         {name: 'Soup', emoji: '🍲'},
         {name: 'Sushi', emoji: '🍣'},
         {name: 'Spaghetti', emoji: '🍝'},
         {name: 'Chicken', emoji: '🍗'}
      ], 
      items: [], 
      id: 0,
      shuffle(array) {
         for (let i = array.length - 1; i > 0; i--) {
            const j = Math.floor(Math.random() * (i + 1));
            [array[i], array[j]] = [array[j], array[i]];
         }
      },
      add(){
         this.id=this.id+1; 
         let idx = Math.floor(Math.random(1)*this.emojis.length);
         this.items.push({name: this.emojis[idx].name, emoji: this.emojis[idx].emoji, id: this.id});
         this.emojis.splice(idx,1);
      },
      remove(id){
         let idx = this.items.findIndex(element => element.id == id); 
         this.items.splice(idx,1);
      }
   }
});

And here's the HTML, notice the `x-animate` attribute on the main DOM element and the DIV that contains all the snacks.


<div class="w-1/2 p-5 border border-black rounded-md " x-data="snacks()" x-animate>
   <div class="flex flex-row justify-between items-center">
      <button class="font-bold border-black border-2 px-4 py-2 rounded-md hover:bg-green-300 transition duration-200 "
         @click="add()">Add A Snack</button>
      <button class="font-bold border-black border-2 px-4 py-2 rounded-md hover:bg-green-300 transition duration-200"
         @click="shuffle(items);">Shuffle</button>
   </div>
   <template x-if="items.length > 0">
      <div class="grid grid-cols-2 gap-4 mt-5 transition duration-500" x-animate>
         <template x-for="item in items" :key="item.id">
            <div class="relative group flex justify-between">
               <div class="flex items-center gap-5">
                  <div
                     class="w-20 h-20 relative rounded-full flex items-center justify-center bg-green-200 border border-green-400">
                     <span class="text-4xl" x-text="item.emoji"></span>
                     <button
                        class=" text-red-500  opacity-0 group-hover:opacity-100 transition duration-200 absolute top-0 left-0 w-full h-full flex items-center justify-center"
                        @click.prevent="remove(item.id)">
                        <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
                           stroke="currentColor" class="w-16 h-16">
                           <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
                        </svg>
                     </button>
                  </div>
                  <span class="text-2xl" x-text="item.name"></span>
               </div>
            </div>
         </template>
      </div>
   </template>
</div>

Setup

import autoAnimate from "@formkit/auto-animate";
import Alpine from "alpinejs";
window.Alpine = Alpine;
Alpine.directive('animate', el => {autoAnimate(el)});
Alpine.start();

That's it. Two lines, one to import and one for the new directive. 

After

Sprinkle in that new AlpineJs directive and an `x-animate` attribute on the list and checkout the results. Pretty crazy upgrade for 2 lines of code. Overall it added 15KB of javascript (4KB gzipped). Completely worth it.

Final word

From the AutoAnimate website:

**********

AutoAnimate is fundamentally a single function — autoAnimate — that accepts a parent element. Automatic animations will be applied to the parent element and its immediate children. Animations are specifically triggered when one of three events occurs:

  • A child is added in the DOM.
  • A child is removed in the DOM.
  • A child is moved in the DOM.

**********

That's really the only thing to keep in mind when mixing it into AlpineJs. It won't work with `x-show` if you are hiding/showing elements, you need to use `x-if` with a `template` so DOM elements are actively being added/removed from some parent element.