Hone logo
Hone
Problems

Vue Transition Hooks: Mastering Component Entry and Exit Animations

Vue.js provides powerful built-in transition components that allow for smooth visual effects when components enter or leave the DOM. This challenge focuses on leveraging Vue's transition hooks to precisely control the timing and behavior of these animations, enabling sophisticated and user-friendly interfaces.

Problem Description

Your task is to implement custom transition hooks for a Vue component. You will be provided with a basic Vue component that conditionally renders based on a boolean flag. You need to use Vue's <transition> component and its associated hooks (before-enter, enter, after-enter, before-leave, leave, after-leave) to create a staggered fade-in and fade-out animation for this component.

Key Requirements:

  1. Conditional Rendering: The component should appear and disappear based on a reactive showComponent boolean.
  2. Fade-In Animation: When the component enters the DOM, it should fade in over 500 milliseconds.
  3. Fade-Out Animation: When the component leaves the DOM, it should fade out over 500 milliseconds.
  4. Staggered Entry: If multiple instances of the component are added rapidly, their fade-in animations should be staggered by 100 milliseconds each.
  5. Staggered Exit: Similarly, if multiple instances are removed rapidly, their fade-out animations should be staggered.
  6. No External Libraries: Use only Vue.js's built-in transition functionality.

Expected Behavior:

  • When showComponent is toggled from false to true, the component should smoothly fade in. If multiple components are added in quick succession, they should animate in one after another.
  • When showComponent is toggled from true to false, the component should smoothly fade out. If multiple components are removed in quick succession, they should animate out one after another.

Edge Cases:

  • Rapidly toggling showComponent on and off.
  • Adding and removing multiple components within the same tick of the event loop.

Examples

Example 1: Basic Fade-In

<template>
  <div>
    <button @click="showComponent = !showComponent">Toggle Component</button>
    <transition
      name="fade"
      @before-enter="onBeforeEnter"
      @enter="onEnter"
      @after-enter="onAfterEnter"
      @before-leave="onBeforeLeave"
      @leave="onLeave"
      @after-leave="onAfterLeave"
    >
      <div v-if="showComponent" class="my-component">
        Hello, Vue Transitions!
      </div>
    </transition>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const showComponent = ref(false);

    // Implement these hooks
    const onBeforeEnter = (el: Element) => { /* ... */ };
    const onEnter = (el: Element, done: () => void) => { /* ... */ };
    const onAfterEnter = (el: Element) => { /* ... */ };
    const onBeforeLeave = (el: Element) => { /* ... */ };
    const onLeave = (el: Element, done: () => void) => { /* ... */ };
    const onAfterLeave = (el: Element) => { /* ... */ };

    return {
      showComponent,
      onBeforeEnter,
      onEnter,
      onAfterEnter,
      onBeforeLeave,
      onLeave,
      onAfterLeave,
    };
  },
});
</script>

<style>
.my-component {
  padding: 20px;
  background-color: lightblue;
  margin-top: 20px;
}
</style>

Expected Output Behavior (Visual):

When the button is clicked, the "Hello, Vue Transitions!" div fades in over 500ms. Clicking again fades it out over 500ms.

Explanation:

The <transition> component is wrapped around the conditionally rendered div. The name="fade" prop will prefix the CSS classes applied during transitions (e.g., .fade-enter-active). The hook functions are provided to gain programmatic control.

Example 2: Staggered Entry

Imagine you have a list of items that are added to the DOM by clicking a button multiple times in quick succession.

<template>
  <div>
    <button @click="addItem">Add Item</button>
    <transition-group
      name="fade"
      tag="ul"
      @before-enter="onBeforeEnter"
      @enter="onEnter"
      @after-enter="onAfterEnter"
      @before-leave="onBeforeLeave"
      @leave="onLeave"
      @after-leave="onAfterLeave"
    >
      <li v-for="item in items" :key="item.id" class="list-item">
        {{ item.text }}
      </li>
    </transition-group>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

let nextItemId = 0;

export default defineComponent({
  setup() {
    const items = ref<{ id: number, text: string }[]>([]);

    const addItem = () => {
      items.value.push({ id: nextItemId++, text: `Item ${nextItemId}` });
    };

    // Implement these hooks with staggering logic
    const onBeforeEnter = (el: Element) => { /* ... */ };
    const onEnter = (el: Element, done: () => void) => { /* ... */ };
    const onAfterEnter = (el: Element) => { /* ... */ };
    const onBeforeLeave = (el: Element) => { /* ... */ };
    const onLeave = (el: Element, done: () => void) => { /* ... */ };
    const onAfterLeave = (el: Element) => { /* ... */ };

    return {
      items,
      addItem,
      onBeforeEnter,
      onEnter,
      onAfterEnter,
      onBeforeLeave,
      onLeave,
      onAfterLeave,
    };
  },
});
</script>

<style>
.list-item {
  padding: 10px;
  background-color: lightgreen;
  margin-bottom: 5px;
}
</style>

Expected Output Behavior (Visual):

When "Add Item" is clicked multiple times quickly, each new item fades in sequentially, with a 100ms delay between the start of each item's animation.

Explanation:

Here, <transition-group> is used for animating lists. The onEnter hook will need to determine which item it is and apply a delay to its animation based on its index.

Constraints

  • All transition durations must be precisely 500ms.
  • Staggering delays must be exactly 100ms between consecutive items.
  • The solution must be implemented in TypeScript.
  • The component's initial state when entering should be opacity: 0.
  • The component's final state when entering should be opacity: 1.
  • The component's initial state when leaving should be opacity: 1.
  • The component's final state when leaving should be opacity: 0.

Notes

  • The el argument passed to the transition hooks is the DOM element being transitioned.
  • The done callback in enter and leave hooks must be called to signal that the transition is complete.
  • For staggered animations, you'll need to find a way to identify the order of entering/leaving elements. The v-for directive often provides an index.
  • Consider how to handle the transition-group's absolute positioning during transitions if you encounter layout shifts (though for simple opacity, it's less of an issue).
  • The name prop on <transition> or <transition-group> is crucial as it prefixes the default CSS classes (like enter-active-class, leave-active-class). You can leverage this or use JS hooks exclusively. For this challenge, focus on the JS hooks.
  • You can use requestAnimationFrame or setTimeout within your JavaScript hooks to manage timing.
Loading editor...
typescript