Кастомные переходы

Vuetify предоставляет встроенную поддержку анимаций через компонент <transition> и его обертки, но для создания уникальных визуальных эффектов необходимы кастомные переходы. Кастомные переходы позволяют контролировать поведение компонентов при их появлении, исчезновении или замене, задавая собственные CSS-анимации или JavaScript-хуки.

Компонент <transition> принимает ключевые свойства:

  • name — префикс для CSS-классов анимации.
  • mode — определяет порядок появления и исчезновения элементов (in-out, out-in).
  • appear — позволяет анимации срабатывать при первоначальном рендере компонента.
  • css — булево значение, которое указывает, использовать CSS-анимации или полностью контролировать через JavaScript.

Пример базового кастомного перехода:

<template>
  <transition name="fade-slide" mode="out-in">
    <div v-if="show" class="box">Контент</div>
  </transition>
</template>

<style>
.fade-slide-enter-active {
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.fade-slide-leave-active {
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.fade-slide-enter-from, .fade-slide-leave-to {
  opacity: 0;
  transform: translateY(-20px);
}
.fade-slide-enter-to, .fade-slide-leave-from {
  opacity: 1;
  transform: translateY(0);
}
</style>

Здесь используются четыре ключевых класса:

  1. *-enter-from — начальное состояние элемента при появлении.
  2. *-enter-to — конечное состояние при появлении.
  3. *-leave-from — начальное состояние при исчезновении.
  4. *-leave-to — конечное состояние при исчезновении.

JavaScript-хуки для переходов

Для более сложных анимаций, которые не ограничиваются CSS, Vuetify позволяет использовать JavaScript-хуки: beforeEnter, enter, afterEnter, beforeLeave, leave, afterLeave. Каждый хук принимает элемент и функцию завершения анимации.

Пример использования:

<template>
  <transition
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
  >
    <div v-if="show" class="box">Контент</div>
  </transition>
</template>

<script>
export default {
  methods: {
    beforeEnter(el) {
      el.style.opacity = 0;
      el.style.transform = 'scale(0.8)';
    },
    enter(el, done) {
      const animation = el.animate(
        [
          { opacity: 0, transform: 'scale(0.8)' },
          { opacity: 1, transform: 'scale(1)' }
        ],
        { duration: 400, easing: 'ease-out' }
      );
      animation.onfin ish = done;
    },
    leave(el, done) {
      const animation = el.animate(
        [
          { opacity: 1, transform: 'scale(1)' },
          { opacity: 0, transform: 'scale(0.8)' }
        ],
        { duration: 300, easing: 'ease-in' }
      );
      animation.onfin ish = done;
    }
  }
};
</script>

Этот подход позволяет создавать анимации с динамическими параметрами, управлять задержками и синхронизировать переходы с внешними событиями.

Переходы для компонентов Vuetify

Большинство компонентов Vuetify, таких как <v-dialog>, <v-menu>, <v-tooltip>, поддерживают свойства transition и origin.

  • transition — имя CSS-класса или компонента перехода.
  • origin — точка, вокруг которой выполняется анимация (top, bottom, center, left, right).

Пример кастомного перехода для диалога:

<v-dialog v-model="dialog" transition="dialog-scale-transition" origin="top center">
  <v-card>
    <v-card-title>Заголовок</v-card-title>
    <v-card-text>Содержимое диалога</v-card-text>
  </v-card>
</v-dialog>

<style>
.dialog-scale-transition-enter-active,
.dialog-scale-transition-leave-active {
  transition: transform 0.3s ease, opacity 0.3s ease;
}
.dialog-scale-transition-enter-from,
.dialog-scale-transition-leave-to {
  transform: scale(0.7);
  opacity: 0;
}
.dialog-scale-transition-enter-to,
.dialog-scale-transition-leave-from {
  transform: scale(1);
  opacity: 1;
}
</style>

Комбинирование нескольких анимаций

Можно объединять несколько анимаций, используя сложные классы и вложенные переходы, либо оборачивая компонент в несколько <transition> блоков. Например, плавное появление списка с задержкой между элементами:

<template>
  <div>
    <transition-group name="list-fade" tag="div">
      <div v-for="item in items" :key="item.id" class="list-item">
        {{ item.text }}
      </div>
    </transition-group>
  </div>
</template>

<style>
.list-fade-enter-active {
  transition: all 0.4s ease;
}
.list-fade-leave-active {
  transition: all 0.3s ease;
  position: absolute;
}
.list-fade-enter-from {
  opacity: 0;
  transform: translateX(-20px);
}
.list-fade-enter-to {
  opacity: 1;
  transform: translateX(0);
}
.list-fade-leave-from {
  opacity: 1;
  transform: translateX(0);
}
.list-fade-leave-to {
  opacity: 0;
  transform: translateX(20px);
}
</style>

<transition-group> автоматически применяет анимации ко всем дочерним элементам с поддержкой ключей (key), что позволяет создавать эффекты последовательного появления элементов списка.

Рекомендации по производительности

  • Использовать CSS-анимации, когда это возможно, так как они оптимизированы браузером.
  • Минимизировать количество анимируемых свойств, отдавая предпочтение transform и opacity.
  • Для больших списков и динамических компонентов применять transition-group с задержкой и requestAnimationFrame для контроля сложных анимаций.
  • При использовании JavaScript-анимаций — корректно вызывать функцию завершения (done) для синхронизации с жизненным циклом компонентов.

Интеграция с темами и стилями Vuetify

Кастомные переходы можно комбинировать с системами тем Vuetify, изменяя цвета, тени и масштаб в зависимости от активной темы. Например, при тёмной теме анимация появления диалога может включать легкую подсветку фона:

.theme--dark .dialog-scale-transition-enter-from {
  background-color: rgba(255, 255, 255, 0.05);
}
.theme--dark .dialog-scale-transition-enter-to {
  background-color: rgba(255, 255, 255, 0.1);
}

Это создаёт ощущение глубины и плавного перехода без необходимости переписывать JavaScript или глобальные CSS-классы.

Кастомные переходы в Vuetify предоставляют гибкий инструмент для создания уникальных визуальных эффектов, позволяя полностью контролировать поведение компонентов при рендере и изменении состояния.