Использование в Vue

Библиотека Vivus предназначена для анимации SVG-путей через последовательное прорисовывание stroke. В экосистеме Vue.js она используется для создания визуально насыщенных интерфейсов, где SVG становится частью реактивного состояния компонентов.

Ключевой принцип интеграции заключается в том, что Vivus работает напрямую с DOM-узлами SVG, поэтому взаимодействие с Vue строится через lifecycle-хуки и ссылки на элементы (ref).


Базовая установка и подключение

Vivus устанавливается как обычная зависимость:

npm install vivus

Варианты подключения в проекте Vue:

import Vivus from 'vivus'

или через динамический импорт для снижения начального веса:

const Vivus = (await import('vivus')).default

Инициализация внутри компонента Vue 3

Работа с SVG требует доступа к DOM после монтирования компонента.

<template>
  <div class="icon-wrapper">
    <svg ref="svgRef" viewBox="0 0 100 100">
      <path d="M10 10 H 90 V 90 H 10 Z" />
    </svg>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import Vivus from 'vivus'

const svgRef = ref(null)

onMounted(() => {
  new Vivus(svgRef.value, {
    type: 'delayed',
    duration: 120,
    animTimingFunction: Vivus.EASE
  })
})
</script>

Vivus требует передать DOM-элемент SVG или его id. В Vue предпочтительно использовать ref, так как он обеспечивает прямую ссылку на узел после рендера.


Режимы анимации Vivus

Vivus поддерживает несколько стратегий отрисовки SVG:

  • delayed — последовательная отрисовка путей с задержкой
  • sync — синхронная анимация всех элементов
  • oneByOne — поэтапная прорисовка каждого path

Пример конфигурации:

new Vivus(svgRef.value, {
  type: 'oneByOne',
  duration: 200,
  start: 'autostart'
})

Каждый режим влияет на восприятие интерфейса: sync подходит для логотипов, oneByOne — для иллюстраций и storytelling-интерфейсов.


Использование через компонент-обёртку

В архитектуре Vue целесообразно вынести Vivus в переиспользуемый компонент.

<template>
  <svg ref="svgRef" :viewBox="viewBox">
    <slot />
  </svg>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import Vivus from 'vivus'

const props = defineProps({
  duration: {
    type: Number,
    default: 100
  },
  type: {
    type: String,
    default: 'delayed'
  },
  viewBox: {
    type: String,
    default: '0 0 100 100'
  }
})

const svgRef = ref(null)

onMounted(() => {
  new Vivus(svgRef.value, {
    type: props.type,
    duration: props.duration,
    start: 'autostart'
  })
})
</script>

Такой компонент позволяет абстрагировать логику анимации и повторно использовать её для любых SVG.


Управление анимацией через реактивные состояния

Vivus можно контролировать через методы экземпляра:

const vivusInstance = ref(null)

onMounted(() => {
  vivusInstance.value = new Vivus(svgRef.value, {
    type: 'delayed',
    duration: 150,
    start: 'manual'
  })
})

Дальнейшее управление:

vivusInstance.value.play()
vivusInstance.value.stop()
vivusInstance.value.reset()

В Vue это связывается с реактивными событиями интерфейса:

<button @click="vivusInstance.play()">Запуск</button>
<button @click="vivusInstance.reset()">Сброс</button>

Работа с динамическими SVG

При использовании динамически генерируемых иконок важно учитывать момент, когда DOM полностью сформирован.

<template>
  <svg v-if="ready" ref="svgRef">
    <path :d="pathData" />
  </svg>
</template>

<script setup>
import { ref, watch, nextTick } from 'vue'
import Vivus from 'vivus'

const props = defineProps({
  pathData: String
})

const svgRef = ref(null)
const ready = ref(false)

watch(() => props.pathData, async () => {
  ready.value = false
  await nextTick()
  ready.value = true

  await nextTick()

  new Vivus(svgRef.value, {
    type: 'oneByOne',
    duration: 120
  })
})
</script>

Ключевым моментом является повторная инициализация после обновления DOM.


Интеграция с директивами Vue

Для масштабируемых проектов удобно использовать пользовательскую директиву.

export default {
  mounted(el, binding) {
    new Vivus(el, binding.value || {
      type: 'delayed',
      duration: 100
    })
  }
}

Регистрация:

app.directive('vivus', vivusDirective)

Использование:

<svg v-vivus="{ type: 'sync', duration: 80 }">
  <path d="..." />
</svg>

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

При работе с большим количеством SVG-анимаций важно учитывать нагрузку на DOM:

  • минимизация количества path-элементов
  • использование упрощённых контуров (SVG optimization)
  • отключение повторной инициализации при ненужных rerender
  • применение v-once для статических SVG

Пример:

<svg v-once ref="svgRef">

Также важно избегать повторного создания экземпляров Vivus без уничтожения предыдущих.


Интеграция с transition и анимациями Vue

Vivus можно сочетать с системой переходов Vue:

<transition name="fade" @after-enter="startAnimation">
  <svg v-if="visible" ref="svgRef">
    <path d="..." />
  </svg>
</transition>
const startAnimation = () => {
  new Vivus(svgRef.value, {
    type: 'delayed',
    duration: 100
  })
}

Это позволяет синхронизировать появление компонента и начало отрисовки SVG.


Работа в составе дизайн-системы

Vivus часто используется для:

  • анимированных логотипов
  • иллюстраций onboarding-процессов
  • интерактивных иконок состояния
  • визуализации прогресса

В Vue дизайн-система может содержать слой абстракции:

  • BaseIconAnimated.vue
  • LogoVivus.vue
  • IllustrationStroke.vue

Каждый компонент инкапсулирует настройки Vivus и SVG-структуру.


Обработка повторного запуска анимации

Для повторной анимации требуется сброс состояния:

const replay = () => {
  vivusInstance.value.reset()
  vivusInstance.value.play()
}

В Vue это связывается с реакцией на изменение маршрута:

watch(() => route.path, () => {
  replay()
})

Особенности SSR и Nuxt

При использовании Nuxt важно учитывать отсутствие DOM на сервере.

if (process.client) {
  new Vivus(svgRef.value, options)
}

или:

onMounted(() => {
  if (!svgRef.value) return
  new Vivus(svgRef.value, options)
})

Это предотвращает ошибки гидратации.


Связка с Composition API архитектурой

В крупных приложениях логика Vivus выносится в composable:

import Vivus from 'vivus'
import { ref, onMounted } from 'vue'

export function useVivus(options) {
  const el = ref(null)
  const instance = ref(null)

  onMounted(() => {
    instance.value = new Vivus(el.value, options)
  })

  return {
    el,
    instance
  }
}

Использование:

const { el, instance } = useVivus({
  type: 'sync',
  duration: 120
})

Структурирование SVG для корректной работы Vivus

Качество анимации напрямую зависит от структуры SVG:

  • каждый логический элемент должен быть отдельным <path>
  • избегается использование <g> с трансформациями
  • оптимизация через инструменты вроде SVGO
  • фиксированный viewBox

Пример корректной структуры:

<svg viewBox="0 0 200 200">
  <path d="..." />
  <path d="..." />
</svg>

Синхронизация с состоянием приложения

Vivus можно связывать с состояниями загрузки:

<svg ref="svgRef" v-if="loading === false">
watch(loading, (val) => {
  if (!val) {
    new Vivus(svgRef.value, { duration: 100 })
  }
})

Это позволяет использовать SVG-анимацию как индикатор завершения процессов.