We use cookies to enhance your experience on the site
CodeWorlds

Advanced Animation Patterns

In NOVA LAB's advanced systems, we need sophisticated effects — cascading dashboard element loading, animation on first page display, precise duration control, and respect for users' motion preferences. Learn the patterns that give interfaces a professional feel.

Appear — Animation on Mount

By default,

<Transition>
only animates the element when toggling
v-if
/
v-show
. The
appear
attribute causes the animation to also run on the first render (mount) of the component:

1<Transition appear name="fade">
2  <div>This element animates on page load</div>
3</Transition>

This is the ideal solution for elements visible right after loading — the station logo, dashboard header, status panel. Without

appear
, these elements would simply "be there" without any enter animation.

You can use special CSS classes for the appear animation (independent from the normal enter classes):

1<Transition
2  appear
3  appear-from-class="appear-start"
4  appear-active-class="appear-running"
5  appear-to-class="appear-end"
6>
7  <div>Custom appear animation</div>
8</Transition>

Or with libraries:

1<Transition
2  appear
3  appear-active-class="animate__animated animate__fadeInDown"
4>
5  <header>NOVA LAB Control Center</header>
6</Transition>

Staggered List Animation

One of the most impressive patterns — cascading appearance of list elements, where each successive element appears with an increasing delay. It looks like data displaying on a NOVA LAB holographic dashboard, element by element:

1<TransitionGroup
2  appear
3  :css="false"
4  @before-enter="onBeforeEnter"
5  @enter="onEnter"
6  tag="div"
7>
8  <div v-for="(item, index) in items" :key="item.id" :data-index="index">
9    {{ item.name }}
10  </div>
11</TransitionGroup>

The key is the

:data-index
attribute — it passes the element index to the JS hook, which calculates the delay:

1function onBeforeEnter(el) {
2  el.style.opacity = 0
3  el.style.transform = 'translateY(20px)'
4}
5
6function onEnter(el, done) {
7  // Each successive element has a 150ms greater delay
8  const delay = el.dataset.index * 150
9
10  setTimeout(() => {
11    el.style.transition = 'all 0.5s ease'
12    el.style.opacity = 1
13    el.style.transform = 'translateY(0)'
14    el.addEventListener('transitionend', done, { once: true })
15  }, delay)
16}

The effect: element 0 appears immediately, element 1 after 150ms, element 2 after 300ms, etc. For 10 elements, the entire animation takes 1.5s + 0.5s (animation duration itself) = ~2s.

Tip: Limit the delay to a max of ~1s so the user doesn't wait too long for the last elements:

1const delay = Math.min(el.dataset.index * 100, 1000)

Explicit Duration

When an element has both CSS transition and animation, or when nested elements have different animation durations, Vue may not correctly detect the end point. The

:duration
attribute lets you explicitly specify the duration in milliseconds:

1<!-- Same duration for enter and leave -->
2<Transition :duration="500">...</Transition>
3
4<!-- Different durations for enter and leave -->
5<Transition :duration="{ enter: 300, leave: 500 }">...</Transition>

This is particularly useful when:

  • Animating nested elements with different durations
  • Combining CSS transition and animation on the same element
  • Using external libraries whose duration isn't automatically detected

Accessibility — prefers-reduced-motion

Respect users' motion settings. People with vestibular disorders or epilepsy may have the

prefers-reduced-motion
preference enabled. Your CSS should honor this:

1/* Normal animations */
2.fade-enter-active,
3.fade-leave-active {
4  transition: opacity 0.3s ease, transform 0.3s ease;
5}
6
7.fade-enter-from,
8.fade-leave-to {
9  opacity: 0;
10  transform: translateY(10px);
11}
12
13/* Disable animations for users who don't want them */
14@media (prefers-reduced-motion: reduce) {
15  .fade-enter-active,
16  .fade-leave-active {
17    transition: none;
18  }
19
20  .fade-enter-from,
21  .fade-leave-to {
22    opacity: 1;
23    transform: none;
24  }
25}

You can also handle this in JavaScript:

1const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
2
3function onEnter(el, done) {
4  if (prefersReducedMotion) {
5    done() // Skip the animation
6    return
7  }
8  // Normal animation...
9}

Performance Best Practices

  1. Only animate

    transform
    and
    opacity
    — these properties are GPU-accelerated and don't cause reflow/repaint. Avoid animating
    width
    ,
    height
    ,
    top
    ,
    left
    ,
    margin
    ,
    padding
    .

  2. Use

    will-change
    for heavy animations — it informs the browser about an upcoming animation, allowing for optimization:

1.heavy-animation-enter-active {
2  will-change: transform, opacity;
3  transition: all 0.5s ease;
4}
  1. Prefer CSS over JS — CSS animations are optimized by the browser and run on a separate thread. Use JS hooks only when you need dynamic values.

  2. Short durations — UI animations should last 200-500ms. Longer animations slow down interaction and frustrate users.

  3. Test on mobile devices — animations that run smoothly on desktop may lag on older phones.

Go to CodeWorlds