Search code examples
javascriptcssvue.jstransitionvue-transitions

Vue Transition - animation-delay behaves wierd/does not work


So I am trying to animate an SVG. When applying animation delay to each path it works properly in v-leave-active class but doesn't work at all in v-enter-active and behaves weird. Check the code below and please help. Run the snippet and click on the button "click" then again click to see the exact problem happening.

const { createApp } = Vue
  createApp({
    data() {
      return {
        show: false
      }
    }
  }).mount('.app')
.svg{
     max-width: 100px;
     margin-left: 50px
  }

/* Enter Animation */
    .forwardOne-enter-active{
      animation: fade-in 0.2s;
    }
    .forwardTwo-enter-active{
      animation: fade-in 0.2s;
      animation-delay: 0.3s;
    }
    .forwardThree-enter-active{
      animation: fade-in 0.2s;
      animation-delay: 0.6s;
    }
    /*Leave Animation*/
    .forwardOne-leave-active {
      animation: fade-in 0.2s reverse;
    }
    .forwardTwo-leave-active {
      animation: fade-in 0.2s reverse;
      animation-delay: 0.3s;
    }
    .forwardThree-leave-active {
      animation: fade-in 0.2s reverse;
      animation-delay: 0.6s;
    }

    @keyframes fade-in {
      0% {
        opacity: 0;
      }
      50% {
        transform: 0.5;
      }
      100% {
        transform: 1;
      }
    }
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<div class="app">
  <button class="right" @click="show = !show">
      Click
  </button>
  <button class="svg">
    <svg fill="#000" viewBox="0 0 24 24" stroke="3px">
      <g>
          <Transition name="forwardThree">
            <path v-show="show" d="M15.14,5.46V18.57l8.32-6.64ZM15.87,7l6.42,5-6.42,5.12Z"/>
          </Transition>
          <Transition name="forwardTwo">
            <path v-show="show" d="M15.51,11.37l-.37-.28L7.86,5.43V18.54l7.28-5.81.73-.58v-.49ZM8.6,17V6.92l6.41,5Z"/>
          </Transition>
          <Transition name="forwardOne">
            <path v-show="show" d="M8.23,11.41l-.37-.29L.54,5.43V18.54l7.32-5.85.37-.29.37-.3v-.41ZM1.28,17V6.92l6.41,5Z"/>
          </Transition>
        </g>
      </svg>
  </button>
 </div>

You can see that first time it does not work but on second click it works properly. I have no idea why this is happening


Solution

  • There are several problems with the code you posted, each of them breaking the animation:

    • the <svg> is rendered outside the <button> (at least in Chrome)
    • the @keyframe is malformed. You're animating from opacity: 0 to transform: 1.
      transform: 1 is not valid and, even if it was, you can't animate a property into another property. What should we expect halfway through the animation? opacform: 0.5?
      You probably want to animate from opacity: 0 to opacity: 1
    • you're not setting opacity: 0 on .{x}-enter-active classes. It is necessary, for the ones which have delay, because while they are being delayed they will have whatever opacity the element has, which, by default, is 1.

    See it working:

    const {
      createApp
    } = Vue
    createApp({
      data() {
        return {
          show: false
        }
      }
    }).mount('#app')
    .svg svg {
      height: 2rem;
      width: auto;
    }
    
    .forwardOne-enter-active,
    .forwardTwo-enter-active,
    .forwardThree-enter-active {
      animation: .2s fade-in;
      opacity: 0;
    }
    .forwardOne-leave-active,
    .forwardTwo-leave-active,
    .forwardThree-leave-active {
      animation: .2s fade-in reverse;
    }
    .forwardTwo-enter-active,
    .forwardTwo-leave-active {
      animation-delay: .3s;
    }
    
    .forwardThree-enter-active,
    .forwardThree-leave-active {
      animation-delay: .6s;
    }
    
    @keyframes fade-in {
      0% {
        opacity: 0;
      }
      100% {
        opacity: 1;
      }
    }
    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
    
    <div id="app">
      <button class="right" @click="show = !show">
          Click
      </button>
      <button class="svg">
        <svg fill="#000" viewBox="0 0 24 24" stroke="3px">
            <Transition name="forwardThree">
              <path v-show="show" d="M15.14,5.46V18.57l8.32-6.64ZM15.87,7l6.42,5-6.42,5.12Z"/>
            </Transition>
            <Transition name="forwardTwo">
              <path v-show="show" d="M15.51,11.37l-.37-.28L7.86,5.43V18.54l7.28-5.81.73-.58v-.49ZM8.6,17V6.92l6.41,5Z"/>
            </Transition>
            <Transition name="forwardOne">
              <path v-show="show" d="M8.23,11.41l-.37-.29L.54,5.43V18.54l7.32-5.85.37-.29.37-.3v-.41ZM1.28,17V6.92l6.41,5Z"/>
            </Transition>
          </svg>
      </button>
    </div>


    And here's a way to avoid the CSS class boilerplate, using SCSS and creative selectors.


    Side notes:

    1. It's sub-par to use a class selector to mount your app. It's not so much it doesn't work, but the fact Vue treats that selector as an ID selector (it mounts the app in the first element matching the selector).
      If you have more than one element with this class, the app will only be mounted in the first one. By using a class selector, no IDE or code validator will warn you about the duplicity of the selector and, as a developer, you probably want to know about it.
      In my mind, using class selectors to mount Vue apps is like using dots in class names. It works, but it's fragile, therefore an open invitation to future subtle bugs and headaches.

    2. technically, it's sub-par to develop in FF:

    • it makes more sense to develop in whatever most users use to view it. This way, if other browsers are rendering it differently, you're shipping a "different" experience than what you intended to fewer users.
    • if your users are developers (like in the case of the question above), the percentage of Chrome users is higher than 65% (closer to 100%), precisely for the reason above. This means that most SO users who looked at your code thought it's completely broken - (try it in Chrome).

    I like Mozilla and I use some of their products. But it makes little sense to use Firefox while developing (unless I'm developing something aimed at FF users, of course).