Плавное переключение контента в Vue: маленький компонент, который сильно улучшает UX

от автора

в
Время чтения: 2 мин.

В интерфейсах постоянно приходится переключать состояние:

  • форма → “успешно отправлено”
  • карточка → подробности
  • заглушка → реальный контент
  • компактный блок → расширенный

Обычно это делают через v-if. И обычно это выглядит… плохо.

Резкий скачок, дергается высота, ломается восприятие.

В этой статье разберём простой, но мощный паттерн — анимированный переключатель контента, который делает интерфейс заметно приятнее.


🧩 Сам компонент

<script setup lang="ts">
defineProps({
  showPrimary: {
    type: Boolean,
    required: true,
  },
})
</script>

<template>
  <div class="toggle-grid">
    <Transition name="expand">
      <div v-if="showPrimary" class="toggle-cell">
        <slot name="primary" />
      </div>
      <div v-else class="toggle-cell">
        <slot name="secondary" />
      </div>
    </Transition>
  </div>
</template>

<style>
.toggle-grid {
  display: grid;
  grid-template-areas: 'stack';
}

.toggle-cell {
  grid-area: stack;
}

.expand-enter-active,
.expand-leave-active {
  transition:
    max-height 0.3s ease,
    opacity 0.2s ease 0.1s;
}

.expand-enter-from,
.expand-leave-to {
  max-height: 0;
  opacity: 0;
}

.expand-enter-to,
.expand-leave-from {
  max-height: 1000px;
  opacity: 1;
}
</style>

🎯 Какую задачу он решает

Этот компонент делает одну вещь:

плавно заменяет один блок другим без скачков интерфейса

То есть вместо:

  • мгновенного исчезновения
  • пересчёта layout
  • дерганий

мы получаем:

  • мягкое схлопывание
  • плавное появление
  • стабильный UI

🚫 Почему v-if — это проблема

Базовый подход:

<div v-if="showPrimary">...</div>
<div v-else>...</div>

Что происходит:

  • DOM меняется мгновенно
  • высота контейнера резко меняется
  • пользователь “теряет” контекст

Особенно заметно:

  • в формах
  • в карточках
  • в списках

⚙️ Как это работает

1. Управление через один флаг

showPrimary: boolean
  • true → основной контент
  • false → альтернативный

Просто и предсказуемо.


2. Слоты вместо жёсткой логики

<slot name="primary" />
<slot name="secondary" />

Компонент:

  • не знает, что внутри
  • работает с любым UI

👉 это делает его переиспользуемым везде


3. Grid-стек вместо absolute

.toggle-grid {
display: grid;
grid-template-areas: 'stack';
}.toggle-cell {
grid-area: stack;
}

Оба состояния:

  • занимают одну и ту же область
  • не “толкают” layout

💡 Это ключ к отсутствию скачков.


4. Анимация через max-height

.expand-enter-from,
.expand-leave-to {
max-height: 0;
opacity: 0;
}.expand-enter-to,
.expand-leave-from {
max-height: 1000px;
opacity: 1;
}

Что происходит:

  • блок “схлопывается” → max-height: 0
  • затем новый “разворачивается”
  • плюс лёгкий fade

👉 ощущается как естественное раскрытие


🧠 Почему именно max-height

Проблема:

height: auto;

❌ нельзя анимировать

Решение:

max-height: 1000px;

✔ работает как “псевдо-auto”

Да, это хак. Но это стандарт индустрии.


💡 Где это реально полезно

1. Формы

<ToggleExpand :showPrimary="!submitted">
<template #primary>
<Form />
</template>
<template #secondary>
<SuccessMessage />
</template>
</ToggleExpand>

2. Карточки

  • краткое описание → подробности

3. Загрузка данных

  • skeleton → контент

4. Фильтры / настройки

  • свернуто → раскрыто

⚠️ Ограничения (важно)

1. Ограничение по высоте

max-height: 1000px;

Если контент выше → обрежется

👉 решение:

  • увеличить значение
  • или делать JS-измерение

2. Не подходит для сложных переходов

Это:

  • не crossfade
  • не одновременная анимация

А именно:
👉 “один ушёл → другой пришёл”


3. Только два состояния

Если нужно:

  • 3+ состояний
  • сложная логика

лучше:

  • динамический компонент
  • state machine

🔥 Почему это крутой паттерн

Потому что он:

  • простой (20 строк)
  • переиспользуемый
  • сильно улучшает UX
  • не требует JS-магии

И главное:

он убирает ощущение “дёрганого интерфейса”


🧪 Как можно улучшить

Если хочется глубже:

  • убрать max-height → считать высоту через JS
  • добавить mode="out-in" в Transition
  • сделать crossfade (через absolute)
  • добавить easing под бренд

📌 Итог

Это один из тех компонентов, которые:

  • не выглядят важными
  • но сильно влияют на восприятие продукта

Ты просто меняешь v-if на этот паттерн —
и интерфейс становится ощутимо приятнее.


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Сколько будет 6 + 9?