Посібник для Vue

Дізнайтеся, як будувати слайдери з @reelkit/vue.

Спершу дотик
Свайп з інерцією та прилипанням
Навігація з клавіатури
Стрілки + Escape
Прокручування колесом
Необов’язково, з дебаунсом
Віртуалізований
10 000+ елементів, 3 у DOM
Indicators
Прокручування точок у стилі Instagram
Програмний API
next(), prev(), goTo() через шаблонний ref
Режим циклу
Нескінченна кругова навігація
Directional
Вертикально або горизонтально
Composition API
<script setup> із композаблами

Базовий слайдер

The <Reel> загортає контролер слайдера з ядра. Використовуйте слот #item щоб рендерити кожен слайд із віртуалізацією — монтуються лише видимі слайди.

App.vue
<script setup lang="ts">
import { Reel, ReelIndicator } from '@reelkit/vue';

const items = [
  { title: 'Virtualized', subtitle: 'Only 3 slides in DOM', color: '#6366f1' },
  { title: 'Touch First', subtitle: 'Native swipe gestures', color: '#8b5cf6' },
  { title: 'Zero Deps', subtitle: 'Tiny bundle size', color: '#7c3aed' },
  { title: 'Keyboard Nav', subtitle: 'Full a11y support', color: '#ec4899' },
  { title: 'SSR Ready', subtitle: 'Works everywhere', color: '#14b8a6' },
  { title: '60fps', subtitle: 'Smooth animations', color: '#f59e0b' },
];

const onAfterChange = (index: number) => {
  console.log('Current index:', index);
};
</script>

<template>
  <Reel
    :count="items.length"
    style="width: 100%; height: 100dvh"
    direction="vertical"
    :enable-wheel="true"
    @after-change="onAfterChange"
  >
    <template #item="{ index, size }">
      <div
        :style="{
          width: size[0] + 'px',
          height: size[1] + 'px',
          background: items[index].color,
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          justifyContent: 'center',
          color: '#fff',
        }"
      >
        <div style="font-size: 1.5rem; font-weight: bold">
          {{ items[index].title }}
        </div>
        <div style="font-size: 0.875rem; opacity: 0.8">
          {{ items[index].subtitle }}
        </div>
      </div>
    </template>

    <div style="position: absolute; right: 12px; top: 50%; transform: translateY(-50%); z-index: 10">
      <ReelIndicator direction="vertical" />
    </div>
  </Reel>
</template>

ReelIndicator

Необов’язковий компонент, що показує індикатори прогресу в стилі Instagram із поточною позицією в слайдері. Якщо він усередині <Reel>, він сам під’єднується до count та active значення через provide/inject у Vue — нічого зв’язувати вручну не треба.

vue-html
<!-- Auto-connect: count and active are inherited from parent Reel -->
<Reel :count="10" :size="[400, 600]">
  <template #item="{ index, size }"> ... </template>
  <ReelIndicator direction="vertical" />
</Reel>

<!-- Manual usage: pass count and active explicitly (e.g. outside a Reel) -->
<ReelIndicator :count="10" :active="currentIndex" />

Імперативний API — шаблонний ref

The <Reel> надає інтерфейс ReelExpose через шаблонний ref. Скористайтеся ref() щоб зберегти посилання й викликати імперативні методи на кшталт next(), prev(), та goTo().

vue
<script setup lang="ts">
import { ref } from 'vue';
import { Reel, type ReelExpose } from '@reelkit/vue';

const items = [
  { title: 'Slide 1', color: '#6366f1' },
  { title: 'Slide 2', color: '#8b5cf6' },
  { title: 'Slide 3', color: '#ec4899' },
];

const sliderRef = ref<ReelExpose | null>(null);
const currentIndex = ref(0);

const onAfterChange = (index: number) => {
  currentIndex.value = index;
};
</script>

<template>
  <Reel
    ref="sliderRef"
    :count="items.length"
    style="width: 100%; height: 100dvh"
    direction="vertical"
    :enable-wheel="true"
    @after-change="onAfterChange"
  >
    <template #item="{ index, size }">
      <div
        :style="{
          width: size[0] + 'px',
          height: size[1] + 'px',
        }"
      >
        {{ items[index].title }}
      </div>
    </template>
  </Reel>

  <div style="position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%)">
    <button
      :disabled="currentIndex === 0"
      @click="sliderRef?.prev()"
    >
      Prev
    </button>
    <button
      :disabled="currentIndex === items.length - 1"
      @click="sliderRef?.next()"
    >
      Next
    </button>
    <button @click="sliderRef?.goTo(2)">Go to 3</button>
  </div>
</template>

Горизонтальний напрямок

Set direction="horizontal" для слайдера зі свайпом ліворуч і праворуч. Напрямок індикатора має збігатися.

vue-html
<Reel
  :count="items.length"
  :size="[400, 300]"
  direction="horizontal"
>
  <template #item="{ index, size }">
    <div :style="{ width: size[0] + 'px', height: size[1] + 'px' }">
      {{ items[index].title }}
    </div>
  </template>

  <ReelIndicator direction="horizontal" />
</Reel>

Auto-sizing

The size необов’язковий. Якщо його не передати, <Reel> сам вимірює свій контейнер через ResizeObserver і підлаштовується під макет, заданий CSS. Розмір контейнера має задавати батьківський елемент — наприклад, flex, grid або явні розміри в CSS.

vue-html
<!-- Explicit size (fixed) -->
<Reel :count="items.length" :size="[400, 600]">
  <template #item="{ index, size }"> ... </template>
</Reel>

<!-- Auto-size (responsive — sized by CSS) -->
<Reel :count="items.length" style="width: 100%; height: 100dvh">
  <template #item="{ index, size }"> ... </template>
</Reel>

Переходи

Передайте пропс transition щоб змінити анімацію слайдів. ReelKit має п’ять переходів, які піддаються tree-shaking: slideTransition (типово), fadeTransition, flipTransition, cubeTransition, та zoomTransition.

vue
<script setup lang="ts">
import { Reel, cubeTransition } from '@reelkit/vue';
</script>

<template>
  <Reel
    :count="items.length"
    :size="[400, 600]"
    :transition="cubeTransition"
  >
    <template #item="{ index, size }"> ... </template>
  </Reel>
</template>

Режим циклу

Увімкніть нескінченну кругову навігацію пропсом loop . Слайдер безшовно переходить з останнього слайда на перший і навпаки.

vue-html
<Reel
  :count="items.length"
  :size="[400, 600]"
  :loop="true"
>
  <template #item="{ index, size }"> ... </template>
</Reel>

Колбеки подій

The <Reel> видає кілька подій для відстеження стану слайдера:

vue
<script setup lang="ts">
import { Reel } from '@reelkit/vue';

const onBeforeChange = (index: number, nextIndex: number, rangeIndex: number) => {
  console.log('Transitioning from', index, 'to', nextIndex);
};

const onAfterChange = (index: number, rangeIndex: number) => {
  console.log('Arrived at slide', index);
};

const onSlideDragStart = (index: number) => {
  console.log('Started dragging slide', index);
};

const onSlideDragEnd = (index: number) => {
  console.log('Stopped dragging slide', index);
};
</script>

<template>
  <Reel
    :count="20"
    style="width: 100%; height: 100dvh"
    @before-change="onBeforeChange"
    @after-change="onAfterChange"
    @slide-drag-start="onSlideDragStart"
    @slide-drag-end="onSlideDragEnd"
  >
    <template #item="{ index, size }"> ... </template>
  </Reel>
</template>

Вбудовані способи навігації:

  • Дотик / свайп: Тягніть, щоб гортати — з інерцією та прилипанням
  • Клавіатура: Стрілки та Escape
  • Колесо миші: Увімкніть :enable-wheel="true"
  • Програмно: Використовуйте шаблонний ref, щоб дістатися next(), prev(), goTo()
vue
<script setup lang="ts">
import { ref } from 'vue';
import { Reel, type ReelExpose } from '@reelkit/vue';

const sliderRef = ref<ReelExpose | null>(null);
</script>

<template>
  <Reel
    ref="sliderRef"
    :count="10"
    :size="[400, 600]"
  >
    <template #item="{ index, size }">
      <div :style="{ width: size[0] + 'px', height: size[1] + 'px' }">
        Slide {{ index + 1 }}
      </div>
    </template>
  </Reel>

  <button @click="sliderRef?.prev()">Prev</button>
  <button @click="sliderRef?.next()">Next</button>
  <button @click="sliderRef?.goTo(5)">Go to 6</button>
</template>

Стан в URL

useOverlayUrlState будує контролер стану в URL для оверлея й повертає його цілком, а ви передаєте його в <LightboxUrlOverlay> як його :controller . Стан відкриття належить адресному рядку, тож прив’язаний оверлей відкривається сам, а посилання — звичайний спосіб його відкрити. Перший запис відсутнього параметра додає один запис в історію, кожен наступний його замінює, тож гортання ніколи не ховає кнопку «назад». Тримайте контролер, щоб читати value/позицією і закривати програмно через set(null), the same low-level write the overlay uses internally on slide change.

vue
<script setup lang="ts">
import { LightboxUrlOverlay, type LightboxItem } from '@reelkit/vue-lightbox';
import { useOverlayUrlState, urlIndexKey } from '@reelkit/vue';

const props = defineProps<{ images: LightboxItem[] }>();

const photo = useOverlayUrlState({
  param: 'photo',
  ...urlIndexKey(() => props.images.length),
});
</script>

<template>
  <!-- Opening is a link — the overlay reads the URL and opens itself. -->
  <RouterLink v-for="(img, i) in props.images" :key="img.src" :to="`?photo=${i}`">
    <img :src="img.src" />
  </RouterLink>

  <LightboxUrlOverlay :controller="photo" :items="props.images" />
</template>

Об’єкт опцій приймає param, codec, та locator (усі три обов’язкові) плюс необов’язковий adapter. The codec та locator — узгоджена пара з однаковим Id, so for a plain ?photo=3 галереї розгортайте ...urlIndexKey(() => props.images.length), який повертає обидві половини одразу. urlIndexKey зіставляє параметр з індексом слайда й обмежує його живою кількістю, яку повертає геттер, тож застарілий або позамежний ?photo=99 відхиляється й сам зникає з URL замість того, щоб відкрити слайд, якого ніхто не називав. Передавайте геттер, а не число: у Vue setup виконується один раз, тож захоплена довжина застаріла б, поки посторінкова стрічка росте. Він загортає createIndexLocator (половину-локатор) і поєднує його з indexCodec. Посторінкова стрічка або галерея з адресацією за ідентичністю передає власну узгоджену пару codec + locator . Повна таблиця опцій — у довіднику API для Vue.

Патерн слота #item

Замість render prop із React у Vue використовується слот #item з областю видимості. Саме він дає віртуалізацію: монтуються лише видимі слайди. Область слота надає три властивості:

vue-html
<template #item="{ index, indexInRange, size }">
  <!--
    index        : number          — absolute slide index (0 to count-1)
    indexInRange  : number          — position in visible window (0, 1, or 2)
    size          : [number, number] — [width, height] of the container
  -->
  <MySlide
    :data="items[index]"
    :style="{ width: size[0] + 'px', height: size[1] + 'px' }"
  />
</template>

Composables

@reelkit/vue дає композабли для типових сценаріїв з оверлеями:

vue
<script setup lang="ts">
import { ref } from 'vue';
import { useBodyLock, useFullscreen, useReelContext } from '@reelkit/vue';

// Lock body scroll when an overlay is open
const isOpen = ref(true);
useBodyLock(isOpen);

// Fullscreen API with cross-browser support
const containerRef = ref<HTMLElement | null>(null);
const { isFullscreen, toggle } = useFullscreen({ elementRef: containerRef });

// Access parent Reel context (when inside a Reel)
const reelContext = useReelContext();
// reelContext?.index  — active slide index signal
// reelContext?.count  — total slide count signal
// reelContext?.goTo() — navigate programmatically

// Bridge a core Subscribable into a reactive Vue ref
import { toVueRef } from '@reelkit/vue';
const index = toVueRef(reelContext!.index); // Ref<number> — re-renders on change
</script>

Головне

  • Composition API

    Import Reel, ReelIndicator, and composables directly into your <script setup> — реєструвати плагін не потрібно

  • слот #item з областю видимості

    Відповідник у Vue для пропса itemBuilder із React — дає віртуалізацію звичним синтаксисом шаблонів

  • Шаблонний ref

    Використовуйте ref<ReelExpose>() для імперативної навігації — колбеки подій не потрібні

  • @after-change

    Emits (index, rangeIndex) — стежить за поточним індексом для оновлення інтерфейсу

  • контекст provide/inject

    ReelIndicator сам під’єднується до батьківського Reel через provide/inject у Vue — жодного протягування пропсів вручну

Поради щодо продуктивності

  • Тримайте шаблони слайдів легкими

    The #item виконується для кожного видимого слайда (зазвичай трьох одночасно). Уникайте важких обчислень і глибоко вкладених структур усередині.

  • Вантажте дані ближче до краю

    Використовуйте @after-change щоб помітити наближення до кінця й підвантажити наступний пакет, доки слайди не скінчилися — так виходить нескінченна стрічка.

  • Тримайте імперативний стан у ref

    Зберігайте посилання ReelExpose та поточний індекс у ref()Vue — це дає точкову реактивність без зайвих перерендерів.

  • Вимикайте колесо на сторінках із прокручуванням

    Set :enable-wheel="false" коли слайдер вбудований у макет із прокручуванням, щоб не перехоплювати прокручування сторінки.

Наступні кроки