Issues with Vue enter transitions not functioning as expected

I am currently involved in a project where I need to implement enter and exit animations for some components. When a component enters the screen, it should come from the bottom, and when it leaves, it should go upwards. The intended functionality is that when I change the :is property of the component tag, the current component moves upwards while the next one comes from the bottom. Here is a snippet of the code:

<template>
  <div class="home">
    <transition name="section">
      <component :is="activeSection"></component>
    </transition>
  </div>
</template>

<script>
import comp1 from './comp1';
import comp2 from './comp2';

export default {
  components: {
    comp1,
    comp2
  },
  data() {
    activeSection: 'comp1'
  }
</script>

<style scoped>
  .section-enter {
    top: 100vh;
  }
  .section-enter-to {
    top: 0vh;
  }
  .section-enter-active {
    animation-name: 'slideIn';
    animation-duration: 1s;
  }
  .section-leave {
    top: 0vh;
  }
  .section-leave-active {
    animation-name: 'slideOut';
    animation-duration: 1s;
  }
  .section-leave-to {
    top: -100vh;
  }


  @keyframes slideIn {
    from {
      top: 100vh;
    }
    to {
      top: 0
    }
  }

  @keyframes slideOut {
    from {
      top: 0vh;
    }
    to {
      top: -100vh;
    }
  }
</style>

However, the issue I'm facing is that the first component slides upwards, but the second one appears immediately without any animation.

If I render one component at a time (rather than replacing one with another using the same action), everything works perfectly fine. I am perplexed about what might be causing this inconsistency in behavior.

Answer №1

Your CSS has a few issues that need to be addressed.

Explaining CSS Transitions and Animations

It's important to understand the distinction between CSS Transitions and CSS Animations. Your current implementation mixes these concepts incorrectly, causing problems in your code.

The keyframes for the `slideIn` animation along with the `.section-enter` and `.section-enter-to` rules essentially perform the same function of transitioning the `.section` into view. However, without a transition rule specifying a time duration, the change happens instantaneously rather than animating smoothly. This same issue applies to the `slideOut` keyframes and `leave` rules as well.

.section-enter {
  top: 100vh;
}
.section-enter-to {
  top: 0;
}
.section-enter-active {
  transition: .5s; /* Make sure this rule is included */
}

.section-leave {
  top: 0;
}
.section-leave-to {
  top: -100vh;
}
.section-leave-active {
  transition: .5s; /* Ensure this rule is present */
}

To fix the problem, remove the keyframes and add the missing rules mentioned above to enable a functional CSS Transition.

Check out demo 1

Utilizing CSS Animations

An alternative approach involves using keyframes in conjunction with CSS Animations, where animations are applied via the `*-active` rules only without the need for `*-enter` / `*-leave` rules. Note that unnecessary quotes within the `animation-name: 'slideIn';` statement in your question would lead to invalid syntax and no animation taking place. Simplify it by using the shorthand `animation: slideIn 1s;` as shown below.

.section-enter-active {
  animation: slideIn 1s;
}
.section-leave-active {
  animation: slideOut 1s;
}

@keyframes slideIn {
  from {
    top: 100vh;
  }
  to {
    top: 0;
  }
}
@keyframes slideOut {
  from {
    top: 0;
  }
  to {
    top: -100vh;
  }
}

View demo 2 here

Tips on Optimizing CSS Transitions

For better animation performance, consider modifying your approach by utilizing translateY instead of transitioning the `top` property. This can lead to improved efficiency in your animations.

/* Assuming initial value of 'top' in .wrapper is 0 */

.section-leave-active,
.section-enter-active {
  transition: .5s;
}
.section-enter {
  transform: translateY(100%);
}
.section-leave-to {
  transform: translateY(-100%);
}

Explore demo 3 for reference

Answer №2

Implement a Mixin for Animation

A big thank you to @tony19 for the insightful explanation.
To simplify the process of repeating logic, it is recommended to utilize a mixin.
Additionally, the functionality of slideIn and slideOut can be consolidated by employing the reverse method:

@mixin animationmixin($type:'animation', $style:'', $duration:1s) {
    
    @keyframes #{$type}-#{$style} { // define animation
        0% { opacity: 1;  transform: none; } // reset style
        100% { @content; } // custom style
    }
    
    .#{$style} { // append '.section'
        &-enter-active, &-leave-active { // include '.section-enter-active', ...
            transition: #{$duration};
        }
        &-enter, &-leave-to {
            animation: #{$type}-#{$style} #{$duration}; // apply animation
        }
        &-leave, &-enter-to {
            animation: #{$type}-#{$style} #{$duration} reverse; // use reverse animation 
        }
    }
}

To implement, follow these steps:

@include animationmixin($style:'section') { // specify custom styling
    transform: translateY(100%);
};

Or try this approach:

@include animationmixin($style:'fade') {
    opacity: 0;
    transform: scale(0.9);
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for shifting nested div containers to the left as the content expands

I have a div containing a dropdown menu, label, and button as shown below: <div class="container "> <div id="User"> <div id="BtnUser"> <div id="dropMe"> <div class="dropdown"> <a onclick="User ...

The terminal does not recognize the nodemon command

My goal is to automate server reloads using nodemon. I have successfully installed it locally and set the start command as nodemon app.js with this code: "scripts": { "start": "nodemon app.js" } Initially, everything was running smoothly. However, ...

What is the process for linking my Next.js application with MongoDB Compass?

Currently, I am working on a project in Next.js called NetMapper where I am developing a web interface for the CLI tool nmap. My main focus right now is creating Sign In/Sign Up forms and storing user information in MongoDB Compass. Despite trying various ...

Dynamic height adjustment for Bootstrap right column

I am struggling to create a layout with a right column using bootstrap. I can't figure out how to achieve the desired arrangement shown in this image. https://i.stack.imgur.com/VaIWD.png I have attempted various methods but the right column does not ...

Placing an Image in Next.js

I've been trying to position my image all the way to the right of the page using CSS properties like position: absolute and right: 0. I've also attempted setting the parent element's position to relative and the image's position to abso ...

Iterating through a JSON object and an array

I've been attempting to iterate through a JSON object but I'm struggling with it. Below is the JSON object I need to work with. This JSON data will be fetched from another website, and my goal is to loop through it to extract certain details. ...

Utilizing Rollup to Bundle a Collection of React Components

I am diving into the world of bundling as I update a compact React component library internally powered by Rollup. Current Status Currently, all components are bundled into a single index.js file. When I import one or more... import { Button, Input } fr ...

What exactly is the function of $fix-mqs within the IE Sass mixins created by Jake Archibald?

I'm really struggling to understand the purpose behind using $fix-mqs in the mentioned link: Although I can grasp how the rest of the mixin functions, this particular part has me stumped. In a project where I've utilized this pattern, everything ...

Display a loader while waiting for an API call to complete within 5 seconds using Angular and RxJS operators. If the API call takes longer

We are actively working to prevent user blockage during file uploads by implementing a method in Angular using RxJS. How can I display a toastr message and hide the loader if the API does not complete within 5 seconds? uploadFile() { this.service.uploa ...

Guide on placing images in a grid using CSS

Exploring a new way to arrange images in grid style with a box underneath. Seeking advice on achieving this specific layout shown in an image. Current progress in positioning can be seen in this screenshot. Desired adjustments include aligning the far ri ...

Concealing inactive select choices on Internet Explorer versions greater than 11 with the help of AngularJS

I am having trouble hiding the disabled options in AngularJS specifically for IE. Check out this fiddle for a better idea: https://jsfiddle.net/s723gqo1/1/ While I have CSS code that works for hiding disabled options in Chrome/Firefox, it doesn't see ...

Incorporating Tabs with HTML, CSS, and jQuery

After searching for a way to enable tab selection on click within my HTML fragment representing a tabbed interface, I was unable to find a solution. I resorted to manually programming it using JavaScript, which did work, but I feel there must be a more p ...

Vue.js filtering by number

Looking to implement filtering in a Vue project. I have an input field that currently filters by store name extracted from a JSON object, which includes both the store name and ID as a number. How can I also filter by store ID? computed: { filtered ...

Retrieve data from a single PHP page and display it on another page

In my project, I am working with three PHP pages: index.php, fetch_data.php, and product_detail.php. The layout of my index.php consists of three columns: filter options, products panel, and detailed description. Whenever a user clicks on a product in th ...

Implementing ngClass with a dynamic ID in the URL condition

I am attempting to create an ngClass condition that adds an active class to a list element. Specifically, I want it to work for both the URLs '/companies' and '/company/:id'. The issue I am facing is that the current setup does not fun ...

Vue - Send the index value as an argument to the onLoad method

How can I pass the v-for index to a method on page load when creating a report without using a select drop down? The usual way is to use @change event with a select drop down, but in this case there is no select drop down. <div v-for="(item, index) i ...

Angular JS form cloning feature

I'm facing a challenge in creating a dynamic form with multiple sections. At the end of the form, I want to include a "Add New Form" button which will duplicate the existing form below it, each with its own save button. What's the most effective ...

Utilize data attributes in VueJS to dynamically style elements

Is there a way to utilize the data() values within the <style>..</style> section? I have experimented with different combinations (using/omitting this, with/without {{brackets}}, etc.) but haven't been successful. Interestingly, when I man ...

When using an `if` statement in CSS to determine if the value of `left` is

Is there a way to trigger an event only when the object reaches a specific percentage of its left position? I am trying to achieve this using jQuery with the .css() method. Here is what I have so far: HTML: <div id="midDiv"><img ..../></di ...

Modifying one input can impact other input elements without any form of direct connection between them

Below is the HTML code snippet: <tr *ngFor="let row of formData; let i = index" [attr.data-index]="i"> <td *ngFor="let rowdata of formData[i]; let j = index" [attr.data-index]="j"> <input type="checkbox" name="row-{{i}}-{{j}}" [ ...