События
События можно подписывать двумя способами: через опцию on при инициализации или через метод on().
Список событий
Единая концепция: каждое событие — одно явление, без дубликатов (например, только created, только resized).
Жизненный цикл
created— после создания и инициализацииbeforeDestroy— перед уничтожениемdestroyed— при уничтоженииrefresh— вызванupdate()(пересчёт размеров/позиций)optionsUpdated— обновлены опции черезupdateOptions()
Смена слайда
beforeSlideChange— перед сменой слайдаslideChangeStart— начало смены (старт анимации)slideChangeEnd— смена завершенаbeforeTransitionStart— перед началом анимации перехода (для loop)transitionStart— начало анимации переходаtransitionEnd— конец анимации переходаscroll— во время прокрутки (тики анимации / драг)progress— прогресс прокрутки 0..1 (только при!loop)reachBeginning— достигнут первый слайдreachEnd— достигнут последний слайд
Взаимодействие
click— клик по слайдуdragStart— начало перетаскиванияdrag— во время перетаскиванияdragEnd— конец перетаскивания
Состояние и обновления
resized— завершилось изменение размера контейнераbreakpoint— смена breakpointlock— слайдер заблокированunlock— слайдер разблокированdisabled— слайдер отключён (вызванdisable())enabled— слайдер включён (вызванenable())
Видимость слайдов
Видимость слайдера (viewport)
sliderVisible— слайдер вошёл в viewport (модуль visibility)sliderHidden— слайдер вышел из viewport
Автопрокрутка (модуль autoplay)
autoplayStart— автопрокрутка запущенаautoplayStop— автопрокрутка остановленаautoplayPause— автопрокрутка на паузеautoplayResume— автопрокрутка возобновленаautoplayProgress— прогресс автопрокрутки 0..1
Бескрутящаяся лента (модуль marquee)
marqueeStart— marquee запущенmarqueeStop— marquee остановленmarqueePause— marquee на паузеmarqueeResume— marquee возобновлён
Видео (модуль video)
videoReady— видео готово к воспроизведениюvideoPlay— видео начало воспроизведениеvideoPause— видео на паузеvideoEnded— видео завершило воспроизведениеvideoProgress— прогресс воспроизведения видео 0..1
Ленивая загрузка
lazyLoaded— изображение успешно загруженоlazyLoadError— ошибка загрузки изображения
Модули навигации
navigation:mounted— стрелки смонтированыpagination:mounted— пагинация смонтированаnavigation:click— клик по миниатюре (модуль thumbs, аргумент: индекс)
Внутренние / для расширений
setTranslate— перед применением transform к контейнеру (для эффектов)beforeLoopFix— перед коррекцией looploopFix— после коррекции loop
Подписка на события
Через опцию on
const slider = new Tvist('.slider', {
on: {
slideChangeStart: (index) => {
console.log('Слайд изменился:', index)
},
resized: () => {
console.log('Размер изменился')
}
}
})Через метод on()
slider.on('slideChangeStart', (index) => {
console.log('Слайд изменился:', index)
})
slider.on('resized', () => {
console.log('Размер изменился')
})Отписка от событий
const handler = (index) => console.log(index)
// Подписка
slider.on('slideChangeStart', handler)
// Отписка от конкретного обработчика
slider.off('slideChangeStart', handler)
// Отписка от всех обработчиков события
slider.off('slideChangeStart')Подписка на одно срабатывание
slider.once('slideChangeStart', (index) => {
console.log('Выполнится только один раз:', index)
})Lifecycle события
События жизненного цикла слайдера.
created
created: (tvist: Tvist) => voidВызывается после создания и инициализации слайдера. К этому моменту на root уже добавлен класс Tvist.CLASSES.created (например, tvist-v1--created). См. Классы состояний на root.
Параметры:
tvist- экземпляр слайдера
Примеры:
const slider = new Tvist('.slider', {
on: {
created: (tvist) => {
console.log('Слайдер создан')
console.log('Количество слайдов:', tvist.slides.length)
// Инициализация дополнительной логики
initCustomFeatures(tvist)
}
}
})beforeDestroy
beforeDestroy: (tvist: Tvist) => voidВызывается перед уничтожением (до очистки модулей и DOM). Класс Tvist.CLASSES.destroyed на root добавляется сразу после этого события.
destroyed
destroyed: (tvist: Tvist) => voidВызывается при уничтожении слайдера. На root к этому моменту уже висит класс Tvist.CLASSES.destroyed (tvist-v1--destroyed). См. Классы состояний на root.
Параметры:
tvist— экземпляр слайдера
Примеры:
slider.on('beforeDestroy', (tvist) => {
// Сохранить состояние перед очисткой
saveState(tvist)
})
slider.on('destroyed', (tvist) => {
console.log('Слайдер уничтожен')
cleanupCustomFeatures(tvist)
})optionsUpdated
optionsUpdated: (tvist: Tvist, newOptions: Partial<TvistOptions>) => voidВызывается после динамического обновления опций через updateOptions().
Параметры:
tvist- экземпляр слайдераnewOptions- объект с обновлёнными опциями
Примеры:
slider.on('optionsUpdated', (tvist, newOptions) => {
console.log('Обновлены опции:', newOptions)
if (newOptions.perPage) {
console.log('Изменено количество слайдов на:', newOptions.perPage)
}
if (newOptions.direction) {
console.log('Изменено направление на:', newOptions.direction)
}
})События навигации
События, связанные с переключением слайдов.
beforeSlideChange
beforeSlideChange: (index: number) => voidВызывается перед началом смены слайда (до начала анимации).
Параметры:
index- индекс целевого слайда
Примеры:
slider.on('beforeSlideChange', (index) => {
console.log('Начинается переход к слайду:', index)
// Можно отменить действия или подготовить UI
showLoadingIndicator()
})slideChangeStart
slideChangeStart: (index: number) => voidВызывается при начале смены слайда (начало анимации).
Параметры:
index- индекс нового активного слайда
Примеры:
slider.on('slideChangeStart', (index) => {
console.log(`Переход к слайду ${index + 1}`)
// Обновление UI
document.querySelector('.current-slide').textContent = index + 1
// Обновление URL
history.pushState(null, '', `#slide-${index}`)
})slideChangeEnd
slideChangeEnd: (index: number) => voidВызывается после завершения смены слайда (конец анимации).
Параметры:
index- индекс нового активного слайда
Примеры:
slider.on('slideChangeEnd', (index) => {
console.log('Завершён переход к слайду:', index)
// Ленивая загрузка для следующего слайда
const nextSlide = slider.slides[index + 1]
if (nextSlide) {
lazyLoadImages(nextSlide)
}
hideLoadingIndicator()
})События взаимодействия
click
click: (index: number, slide: HTMLElement, event: MouseEvent) => voidКлик по слайду (делегирование с контейнера). Аргументы: индекс слайда, элемент слайда, объект события.
dragStart
dragStart: () => voidВызывается при начале перетаскивания (mousedown или touchstart).
Примеры:
slider.on('dragStart', () => {
console.log('Начато перетаскивание')
// Приостановить автопрокрутку
slider.root.classList.add('is-dragging')
// Отключить другие взаимодействия
disableOtherInteractions()
})drag
drag: () => voidВызывается во время перетаскивания (mousemove или touchmove).
Примеры:
slider.on('drag', () => {
// Обновление индикаторов
updateDragIndicator()
// Отслеживание аналитики
trackDragDistance()
})dragEnd
dragEnd: () => voidВызывается при завершении перетаскивания (mouseup или touchend).
Примеры:
slider.on('dragEnd', () => {
console.log('Завершено перетаскивание')
slider.root.classList.remove('is-dragging')
// Возобновить другие взаимодействия
enableOtherInteractions()
})События обновления
События, связанные с изменением состояния слайдера.
scroll
scroll: () => voidВызывается во время прокрутки (тики анимации или при драге). Для прогресса 0..1 используйте событие progress.
Примеры:
slider.on('scroll', () => {
// Обновление parallax эффекта
updateParallax()
// Обновление прогресс-бара
const progress = Math.abs(slider.engine.location.get())
updateProgressBar(progress)
})resized
resized: () => voidВызывается после завершения изменения размера контейнера (throttle).
Примеры:
slider.on('resized', () => {
console.log('Размер контейнера изменился')
const width = slider.root.offsetWidth
if (width < 768) {
slider.updateOptions({ perPage: 1 })
} else {
slider.updateOptions({ perPage: 3 })
}
})refresh
refresh: () => voidВызывается при вызове update() — пересчитаны размеры и позиции.
beforeTransitionStart
beforeTransitionStart: (data: { index: number; direction: 'next' | 'prev' }) => voidВызывается перед началом анимации перехода. Используется внутренне для loop (коррекция индекса). Полезно для синхронизации с другими слайдерами или кастомной логики по направлению.
transitionStart / transitionEnd
transitionStart: (index: number) => void
transitionEnd: (index: number) => voidНачало и конец анимации перехода к слайду. При мгновенном переходе (instant: true) не вызываются.
progress
progress: (progress: number) => voidПрогресс прокрутки от 0 до 1. Вызывается только при loop: false (в loop нет однозначного прогресса).
reachBeginning / reachEnd
reachBeginning: () => void
reachEnd: () => voidВызываются при достижении первого или последнего слайда (после slideChangeEnd).
breakpoint
breakpoint: (breakpoint: number | null) => voidВызывается при смене breakpoint (активация адаптивных настроек).
Параметры:
breakpoint- значение активного breakpoint в пикселях, илиnullдля базовых настроек
Примеры:
slider.on('breakpoint', (breakpoint) => {
if (breakpoint === null) {
console.log('Активны базовые настройки')
} else {
console.log(`Активен breakpoint: ${breakpoint}px`)
}
// Обновление UI в зависимости от breakpoint
updateUIForBreakpoint(breakpoint)
})lock
lock: () => voidВызывается при блокировке слайдера (когда весь контент помещается в контейнер и прокрутка не нужна). На root при этом уже висит класс Tvist.CLASSES.locked (например, tvist-v1--locked). Подробнее см. Свойства → root, классы состояний.
Примеры:
slider.on('lock', () => {
console.log('Слайдер заблокирован')
// Скрыть навигационные элементы
document.querySelector('.slider-nav').style.display = 'none'
// Класс tvist-v1--locked уже добавлен на root; при необходимости можно добавить свой
slider.root.classList.add('my-locked-state')
})unlock
unlock: () => voidВызывается при разблокировке слайдера. Класс Tvist.CLASSES.locked с root при этом снимается автоматически.
Примеры:
slider.on('unlock', () => {
console.log('Слайдер разблокирован')
// Показать навигационные элементы
document.querySelector('.slider-nav').style.display = ''
// Убрать свой класс (tvist-v1--locked снимается библиотекой)
slider.root.classList.remove('my-locked-state')
})disabled
disabled: (tvist: Tvist) => voidВызывается при отключении слайдера через disable() (добавляется класс, блокируются события).
Параметры:
tvist— экземпляр слайдера
enabled
enabled: (tvist: Tvist) => voidВызывается при включении слайдера через enable() после вызова disable().
Параметры:
tvist— экземпляр слайдера
visible / hidden
visible: (slide: HTMLElement, index: number) => void
hidden: (slide: HTMLElement, index: number) => voidСлайд вошёл в видимую область или вышел из неё (по viewport контейнера). Эмитит модуль slide-states.
sliderVisible / sliderHidden
sliderVisible: () => void
sliderHidden: () => voidСлайдер целиком вошёл в viewport страницы или вышел из него. Эмитит модуль visibility (при опции visibility: true). Используется для паузы/возобновления autoplay и marquee при уходе слайдера из зоны видимости.
Примеры:
slider.on('sliderVisible', () => {
console.log('Слайдер виден на экране')
// Можно возобновить фоновые процессы
})
slider.on('sliderHidden', () => {
console.log('Слайдер скрыт (вкладка или прокрутка)')
// Автопрокрутка и marquee приостанавливаются модулем автоматически
})События Autoplay модуля
События модуля автопрокрутки (при опции autoplay: true или объекте настроек).
autoplayStart
autoplayStart: () => voidВызывается при запуске автопрокрутки (вызов getModule('autoplay').getAutoplay().start() или старт по опциям).
autoplayStop
autoplayStop: () => voidВызывается при полной остановке автопрокрутки.
autoplayPause
autoplayPause: () => voidВызывается при постановке автопрокрутки на паузу (например, при наведении, если pauseOnHover: true).
autoplayResume
autoplayResume: () => voidВызывается при возобновлении автопрокрутки после паузы.
autoplayProgress
autoplayProgress: (data: { progress: number; index: number }) => voidПрогресс до следующего перехода: progress от 0 до 1, index — текущий активный слайд. Вызывается во время ожидания перед сменой слайда (и при ожидании окончания видео, если waitForVideo: true).
Примеры:
slider.on('autoplayProgress', ({ progress, index }) => {
document.querySelector('.progress-bar').style.width = `${progress * 100}%`
})События Marquee модуля
События бескрутящейся ленты (при опции marquee: true или объекте настроек).
marqueeStart
marqueeStart: () => voidВызывается при запуске marquee.
marqueeStop
marqueeStop: () => voidВызывается при остановке marquee.
marqueePause
marqueePause: () => voidВызывается при постановке marquee на паузу (например, при выходе слайдера из viewport).
marqueeResume
marqueeResume: () => voidВызывается при возобновлении marquee.
События Video модуля
События модуля видео (при опции video: true или объекте настроек). Полезны для синхронизации с внешним UI или аналитикой.
videoReady
videoReady: (data: VideoEvent) => voidВызывается, когда видео готово к воспроизведению (событие canplay). VideoEvent: { slide, video, index }.
videoPlay
videoPlay: (data: VideoEvent) => voidВызывается при начале воспроизведения видео.
videoPause
videoPause: (data: VideoEvent) => voidВызывается при постановке видео на паузу.
videoEnded
videoEnded: (data: VideoEvent) => voidВызывается при окончании воспроизведения видео.
videoProgress
videoProgress: (data: VideoProgressEvent) => voidПрогресс воспроизведения: VideoProgressEvent содержит slide, video, index, progress (0..1), currentTime, duration.
Примеры:
slider.on('videoPlay', ({ video, index }) => {
console.log('Воспроизведение слайда', index)
})
slider.on('videoProgress', ({ progress, currentTime, duration }) => {
document.querySelector('.video-progress').style.width = `${progress * 100}%`
})События навигации и модулей
navigation:mounted
Вызывается после монтирования стрелок навигации (модуль navigation). Без параметров.
pagination:mounted
Вызывается после монтирования пагинации (модуль pagination). Без параметров.
navigation:click
'navigation:click': (index: number) => voidВызывается при клике по миниатюре в режиме thumbs (модуль thumbs). Передаётся индекс слайда, к которому выполняется переход.
Примеры:
slider.on('navigation:click', (index) => {
console.log('Выбрана миниатюра:', index)
})События для расширений
setTranslate
setTranslate: (tvist: Tvist, position: number) => voidВызывается в Engine перед применением transform к контейнеру слайдов. Позиция в пикселях. Используется модулем эффектов (fade, cube и т.д.) для кастомной отрисовки. Подписываться только если нужно изменить способ применения трансформа (например, кастомный эффект).
beforeLoopFix
Вызывается в LoopModule перед выполнением коррекции индекса/позиции в режиме loop. Используется внутренне модулями (pagination, drag). Обычно не нужен в пользовательском коде.
loopFix
Вызывается в LoopModule после выполнения коррекции loop. Модули (pagination, drag) подписываются для обновления состояния. Обычно не нужен в пользовательском коде.
События LazyLoad модуля
События модуля ленивой загрузки изображений (требуется опция lazy: true).
lazyLoaded
lazyLoaded: (img: HTMLImageElement, slideIndex: number) => voidВызывается когда изображение успешно загружено.
Параметры:
img(HTMLImageElement) - загруженное изображениеslideIndex(number) - индекс слайда, в котором находится изображение
Примеры:
const slider = new Tvist('.slider', {
lazy: true,
on: {
lazyLoaded: (img, slideIndex) => {
console.log(`Изображение загружено в слайде ${slideIndex}`)
// Добавить класс для анимации появления
img.classList.add('loaded')
// Отслеживание в аналитике
analytics.track('Image Loaded', {
src: img.src,
slideIndex: slideIndex,
loadTime: performance.now()
})
// Показать уведомление
showNotification(`Слайд ${slideIndex + 1} готов к просмотру`)
}
}
})Пример с анимацией:
slider.on('lazyLoaded', (img, slideIndex) => {
// Плавное появление изображения
img.style.opacity = '0'
img.style.transition = 'opacity 0.3s ease'
setTimeout(() => {
img.style.opacity = '1'
}, 10)
})Пример со счётчиком:
let loadedCount = 0
const totalImages = document.querySelectorAll('img[data-src]').length
slider.on('lazyLoaded', (img, slideIndex) => {
loadedCount++
const progress = (loadedCount / totalImages) * 100
document.querySelector('.load-progress').textContent =
`Загружено: ${loadedCount} из ${totalImages} (${progress.toFixed(0)}%)`
if (loadedCount === totalImages) {
console.log('✅ Все изображения загружены!')
hideLoader()
}
})lazyLoadError
lazyLoadError: (img: HTMLImageElement, slideIndex: number) => voidВызывается при ошибке загрузки изображения (404, network error и т.д.).
Параметры:
img(HTMLImageElement) - изображение, которое не удалось загрузитьslideIndex(number) - индекс слайда, в котором находится изображение
Примеры:
const slider = new Tvist('.slider', {
lazy: true,
on: {
lazyLoadError: (img, slideIndex) => {
console.error(`Ошибка загрузки в слайде ${slideIndex}`)
// Установить изображение-заглушку
img.src = '/images/placeholder.jpg'
img.alt = 'Изображение недоступно'
// Добавить класс ошибки
img.classList.add('load-error')
// Логирование в систему мониторинга
errorLogger.log({
type: 'lazy_load_error',
slide: slideIndex,
src: img.dataset.src,
timestamp: Date.now()
})
}
}
})Пример с повторной попыткой:
const MAX_RETRIES = 3
const retryCount = new WeakMap()
slider.on('lazyLoadError', (img, slideIndex) => {
const attempts = retryCount.get(img) || 0
if (attempts < MAX_RETRIES) {
retryCount.set(img, attempts + 1)
console.log(`Попытка ${attempts + 1} из ${MAX_RETRIES}`)
// Повторная попытка через 2 секунды
setTimeout(() => {
const originalSrc = img.dataset.src
if (originalSrc) {
img.src = originalSrc + `?retry=${attempts + 1}`
}
}, 2000)
} else {
// После всех попыток показать placeholder
img.src = '/images/error-placeholder.jpg'
showErrorMessage(`Не удалось загрузить изображение в слайде ${slideIndex + 1}`)
}
})Пример с альтернативными источниками:
slider.on('lazyLoadError', (img, slideIndex) => {
// Попробовать альтернативные CDN
const fallbackCDNs = [
'https://cdn1.example.com',
'https://cdn2.example.com',
'https://cdn3.example.com'
]
const originalSrc = img.dataset.src
const currentCDN = img.dataset.currentCdn || 0
if (currentCDN < fallbackCDNs.length) {
const newSrc = originalSrc.replace(/^https?:\/\/[^\/]+/, fallbackCDNs[currentCDN])
img.dataset.currentCdn = currentCDN + 1
img.src = newSrc
console.log(`Пробуем CDN ${currentCDN + 1}: ${newSrc}`)
} else {
// Все CDN не работают
img.src = '/images/placeholder.jpg'
notifyAdmin(`Image load failed for slide ${slideIndex}`, originalSrc)
}
})Комбинированный пример:
const imageStats = {
loaded: 0,
errors: 0,
total: 0
}
slider.on('created', () => {
imageStats.total = document.querySelectorAll('img[data-src]').length
})
slider.on('lazyLoaded', (img, slideIndex) => {
imageStats.loaded++
updateLoadStats()
// Добавить fade-in эффект
img.style.animation = 'fadeIn 0.3s ease'
})
slider.on('lazyLoadError', (img, slideIndex) => {
imageStats.errors++
updateLoadStats()
// Показать placeholder
img.src = '/images/placeholder.jpg'
img.classList.add('error')
// Уведомление пользователю
if (imageStats.errors > 3) {
showNotification('Проблемы с загрузкой изображений. Проверьте подключение к интернету.')
}
})
function updateLoadStats() {
const total = imageStats.total
const loaded = imageStats.loaded
const errors = imageStats.errors
const pending = total - loaded - errors
console.log(`📊 Статистика: загружено ${loaded}, ошибок ${errors}, ожидает ${pending}`)
document.querySelector('.stats').innerHTML = `
<div>✅ Загружено: ${loaded}</div>
<div>❌ Ошибки: ${errors}</div>
<div>⏳ Ожидает: ${pending}</div>
`
}Примеры использования
Ленивая загрузка изображений (с модулем LazyLoad)
// Используйте встроенный модуль LazyLoad
const slider = new Tvist('.slider', {
lazy: {
preloadPrevNext: 1 // Предзагрузка соседних слайдов
},
on: {
lazyLoaded: (img, slideIndex) => {
console.log(`Изображение загружено в слайде ${slideIndex}`)
// Плавное появление
img.style.opacity = '0'
setTimeout(() => {
img.style.opacity = '1'
}, 10)
},
lazyLoadError: (img, slideIndex) => {
// Замена на placeholder при ошибке
img.src = '/images/placeholder.jpg'
console.error(`Ошибка загрузки в слайде ${slideIndex}`)
}
}
})
// Публичное API для ручной загрузки
const lazyModule = slider.modules.get('lazyload')
// Загрузить все оставшиеся изображения
lazyModule.loadAll()
// Загрузить конкретный слайд
lazyModule.loadSlide(5)HTML разметка:
<div class="tvist-v1__slide">
<img
data-src="image.jpg"
data-srcset="image-400.jpg 400w, image-800.jpg 800w"
alt="Описание"
>
</div>Отслеживание просмотров в аналитике
slider.on('slideChangeEnd', (index) => {
// Google Analytics
gtag('event', 'slide_view', {
slide_index: index,
slide_id: slider.slides[index].dataset.id
})
// Или любая другая аналитика
analytics.track('Slide Viewed', {
index: index,
realIndex: slider.realIndex,
timestamp: Date.now()
})
})Синхронизация с другими элементами
const thumbnails = document.querySelectorAll('.thumbnail')
slider.on('slideChangeStart', (index) => {
// Обновить активную миниатюру
thumbnails.forEach((thumb, i) => {
thumb.classList.toggle('active', i === index)
})
// Обновить текстовое описание
const description = document.querySelector('.slide-description')
description.textContent = slider.slides[index].dataset.description
})Управление видео
slider.on('slideChangeStart', (index) => {
// Остановить видео на предыдущих слайдах
slider.slides.forEach((slide, i) => {
if (i !== index) {
const video = slide.querySelector('video')
if (video) {
video.pause()
video.currentTime = 0
}
}
})
// Воспроизвести видео на текущем слайде
const currentVideo = slider.slides[index].querySelector('video')
if (currentVideo) {
currentVideo.play()
}
})Прогресс-бар
function updateProgress() {
const total = slider.slides.length
const current = slider.activeIndex
const progress = ((current + 1) / total) * 100
document.querySelector('.progress-bar').style.width = `${progress}%`
document.querySelector('.progress-text').textContent =
`${current + 1} / ${total}`
}
slider.on('slideChangeStart', updateProgress)
slider.on('created', updateProgress)Кастомная анимация элементов
slider.on('slideChangeStart', (index) => {
const currentSlide = slider.slides[index]
// Анимация заголовка
const title = currentSlide.querySelector('.slide-title')
if (title) {
title.style.animation = 'none'
setTimeout(() => {
title.style.animation = 'fadeInUp 0.6s ease'
}, 10)
}
// Анимация описания с задержкой
const description = currentSlide.querySelector('.slide-description')
if (description) {
description.style.animation = 'none'
setTimeout(() => {
description.style.animation = 'fadeInUp 0.6s ease 0.2s'
}, 10)
}
})Автопауза при выходе из viewport
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (!entry.isIntersecting) {
const autoplay = slider.getModule('autoplay')
autoplay?.pause()
} else {
const autoplay = slider.getModule('autoplay')
autoplay?.start()
}
})
})
slider.on('created', () => {
observer.observe(slider.root)
})
slider.on('destroyed', () => {
observer.disconnect()
})