589 lines
21 KiB
JavaScript
589 lines
21 KiB
JavaScript
/**
|
||
* @class ItcSlider
|
||
* @version 1.0.1
|
||
* @author https://github.com/itchief
|
||
* @copyright Alexander Maltsev 2020 - 2023
|
||
* @license MIT (https://github.com/itchief/ui-components/blob/master/LICENSE)
|
||
* @tutorial https://itchief.ru/javascript/slider
|
||
*/
|
||
|
||
class ItcSlider {
|
||
static #EL_WRAPPER = 'wrapper';
|
||
static #EL_ITEMS = 'items';
|
||
static #EL_ITEM = 'item';
|
||
static #EL_ITEM_ACTIVE = 'item-active';
|
||
static #EL_INDICATOR = 'indicator';
|
||
static #EL_INDICATOR_ACTIVE = 'indicator-active';
|
||
static #BTN_PREV = 'btn-prev';
|
||
static #BTN_NEXT = 'btn-next';
|
||
static #BTN_HIDE = 'btn-hide';
|
||
static #TRANSITION_NONE = 'transition-none';
|
||
static #SWIPE_THRESHOLD = 20;
|
||
|
||
static #instances = [];
|
||
|
||
static checkSupportPassiveEvents() {
|
||
let passiveSupported = false;
|
||
try {
|
||
const options = Object.defineProperty({}, 'passive', {
|
||
get() {
|
||
passiveSupported = true;
|
||
},
|
||
});
|
||
window.addEventListener('testPassiveListener', null, options);
|
||
window.removeEventListener('testPassiveListener', null, options);
|
||
} catch (error) {
|
||
passiveSupported = false;
|
||
}
|
||
return passiveSupported;
|
||
}
|
||
|
||
#config;
|
||
#state;
|
||
#resizeObserver;
|
||
|
||
/**
|
||
* @param {HTMLElement} el
|
||
* @param {Object} config
|
||
* @param {String} prefix
|
||
*/
|
||
constructor(el, config = {}, prefix = 'itc-slider-') {
|
||
this.#state = {
|
||
prefix, // префикс для классов
|
||
el, // элемент который нужно активировать как ItcSlider
|
||
elWrapper: el.querySelector(`.${prefix}${this.constructor.#EL_WRAPPER}`), // элемент с #CLASS_WRAPPER
|
||
elItems: el.querySelector(`.${prefix}${this.constructor.#EL_ITEMS}`), // элемент, в котором находятся слайды
|
||
elListItem: el.querySelectorAll(`.${prefix}${this.constructor.#EL_ITEM}`), // список элементов, являющиеся слайдами
|
||
btnPrev: el.querySelector(`.${prefix}${this.constructor.#BTN_PREV}`), // кнопка, для перехода к предыдущему слайду
|
||
btnNext: el.querySelector(`.${prefix}${this.constructor.#BTN_NEXT}`), // кнопка, для перехода к следующему слайду
|
||
btnClassHide: prefix + this.constructor.#BTN_HIDE, // класс для скрытия кнопки
|
||
exOrderMin: 0,
|
||
exOrderMax: 0,
|
||
exItemMin: null,
|
||
exItemMax: null,
|
||
exTranslateMin: 0,
|
||
exTranslateMax: 0,
|
||
direction: 'next', // направление смены слайдов
|
||
intervalId: null, // id таймера
|
||
isSwiping: false,
|
||
swipeX: 0,
|
||
swipeY: 0,
|
||
};
|
||
|
||
this.#resizeObserver = null;
|
||
|
||
this.#config = {
|
||
loop: true, direction: 'next', autoplay: false, interval: 5000, refresh: true, swipe: true, ...config
|
||
};
|
||
|
||
this.#init();
|
||
this.#attachEvents();
|
||
}
|
||
|
||
/**
|
||
* Статический метод, который возвращает экземпляр ItcSlider, связанный с DOM-элементом
|
||
* @param {HTMLElement} elSlider
|
||
* @returns {?ItcSlider}
|
||
*/
|
||
static getInstance(elSlider) {
|
||
const found = this.#instances.find((el) => el.target === elSlider);
|
||
if (found) {
|
||
return found.instance;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* @param {String|HTMLElement} target
|
||
* @param {Object} config
|
||
* @param {String} prefix
|
||
*/
|
||
static getOrCreateInstance(target, config = {}, prefix = 'itc-slider-') {
|
||
const elSlider = typeof target === 'string' ? document.querySelector(target) : target;
|
||
const result = this.getInstance(elSlider);
|
||
if (result) {
|
||
return result;
|
||
}
|
||
const slider = new this(elSlider, config, prefix);
|
||
this.#instances.push({ target: elSlider, instance: slider });
|
||
return slider;
|
||
}
|
||
|
||
// статический метод для активирования элементов как ItcSlider на основе data-атрибутов
|
||
static createInstances() {
|
||
document.querySelectorAll('[data-slider="itc-slider"]').forEach((el) => {
|
||
const { dataset } = el;
|
||
const params = {};
|
||
Object.keys(dataset).forEach((key) => {
|
||
if (key === 'slider') {
|
||
return;
|
||
}
|
||
let value = dataset[key];
|
||
value = Number.isNaN(Number(value)) ? value : Number(value);
|
||
value = value === 'true' ? true : value;
|
||
value = value === 'false' ? false : value;
|
||
params[key] = value;
|
||
});
|
||
this.getOrCreateInstance(el, params);
|
||
});
|
||
}
|
||
|
||
slideNext() {
|
||
this.#state.direction = 'next';
|
||
this.#move();
|
||
}
|
||
|
||
slidePrev() {
|
||
this.#state.direction = 'prev';
|
||
this.#move();
|
||
}
|
||
|
||
slideTo(index) {
|
||
this.#moveTo(index);
|
||
}
|
||
|
||
reset() {
|
||
this.#reset();
|
||
}
|
||
|
||
get autoplay() {
|
||
return {
|
||
// Start autoplay
|
||
start: () => {
|
||
this.#config.autoplay = true;
|
||
this.#autoplay();
|
||
},
|
||
// Stop autoplay
|
||
stop: () => {
|
||
this.#autoplay('stop');
|
||
this.#config.autoplay = false;
|
||
}
|
||
};
|
||
}
|
||
|
||
dispose() {
|
||
this.#detachEvents();
|
||
const transitionNoneClass = this.#state.prefix + this.constructor.#TRANSITION_NONE;
|
||
const activeClass = this.#state.prefix + this.constructor.#EL_ITEM_ACTIVE;
|
||
this.#autoplay('stop');
|
||
this.#state.elItems.classList.add(transitionNoneClass);
|
||
this.#state.elItems.style.transform = '';
|
||
this.#state.elListItem.forEach((el) => {
|
||
el.style.transform = '';
|
||
el.classList.remove(activeClass);
|
||
});
|
||
const selIndicators = `${this.#state.prefix}${this.constructor.#EL_INDICATOR_ACTIVE}`;
|
||
document.querySelectorAll(`.${selIndicators}`).forEach((el) => {
|
||
el.classList.remove(selIndicators);
|
||
});
|
||
this.#state.elItems.offsetHeight;
|
||
this.#state.elItems.classList.remove(transitionNoneClass);
|
||
const index = this.constructor.#instances.findIndex((el) => el.target === this.#state.el);
|
||
this.constructor.#instances.splice(index, 1);
|
||
}
|
||
|
||
#onClick(e) {
|
||
if (this.#state.isMoving) {
|
||
e.preventDefault();
|
||
}
|
||
if (!(e.target.closest('.itc-slider-btn') || e.target.closest('.itc-slider-indicators'))) {
|
||
return;
|
||
}
|
||
const classBtnPrev = this.#state.prefix + this.constructor.#BTN_PREV;
|
||
const classBtnNext = this.#state.prefix + this.constructor.#BTN_NEXT;
|
||
this.#autoplay('stop');
|
||
if (e.target.closest(`.${classBtnPrev}`) || e.target.closest(`.${classBtnNext}`)) {
|
||
this.#state.direction = e.target.closest(`.${classBtnPrev}`) ? 'prev' : 'next';
|
||
this.#move();
|
||
} else if (e.target.dataset.slideTo) {
|
||
const index = parseInt(e.target.dataset.slideTo, 10);
|
||
this.#moveTo(index);
|
||
}
|
||
this.#config.loop ? this.#autoplay() : null;
|
||
}
|
||
|
||
#onMouseEnter() {
|
||
this.#autoplay('stop');
|
||
}
|
||
|
||
#onMouseLeave() {
|
||
this.#autoplay();
|
||
}
|
||
|
||
#onTransitionStart() {
|
||
if (this.#config.loop) {
|
||
if (this.#state.isBalancing) {
|
||
return;
|
||
}
|
||
this.#state.isBalancing = true;
|
||
window.requestAnimationFrame(() => {
|
||
this.#balanceItems(false);
|
||
});
|
||
}
|
||
}
|
||
|
||
#onTransitionEnd() {
|
||
if (this.#config.loop) {
|
||
this.#state.isBalancing = false;
|
||
}
|
||
}
|
||
|
||
#onDragStart(e) {
|
||
e.preventDefault();
|
||
}
|
||
|
||
#onVisibilityChange() {
|
||
if (document.visibilityState === 'hidden') {
|
||
this.#autoplay('stop');
|
||
} else if (document.visibilityState === 'visible' && this.#config.loop) {
|
||
this.#autoplay();
|
||
}
|
||
}
|
||
|
||
#touchStart(e) {
|
||
this.#state.isMoving = false;
|
||
this.#autoplay('stop');
|
||
const event = e.type.search('touch') === 0 ? e.touches[0] : e;
|
||
this.#state.swipeX = event.clientX;
|
||
this.#state.swipeY = event.clientY;
|
||
this.#state.isSwiping = true;
|
||
this.#state.isTouchMoving = false;
|
||
}
|
||
|
||
#touchEnd(e) {
|
||
if (!this.#state.isSwiping) {
|
||
return;
|
||
}
|
||
const event = e.type.search('touch') === 0 ? e.changedTouches[0] : e;
|
||
const wrapperRect = this.#state.elWrapper.getBoundingClientRect();
|
||
let clientX = event.clientX < wrapperRect.left ? wrapperRect.left : event.clientX;
|
||
clientX = clientX > wrapperRect.right ? wrapperRect.right : clientX;
|
||
let diffPosX = this.#state.swipeX - clientX;
|
||
if (diffPosX === 0) {
|
||
this.#state.isSwiping = false;
|
||
return;
|
||
}
|
||
if (!this.#config.loop) {
|
||
const isNotMoveFirst = this.#state.activeItems[0] === 1 && diffPosX <= 0;
|
||
const isNotMoveLast = this.#state.activeItems[this.#state.activeItems.length - 1] && diffPosX >= 0;
|
||
if (isNotMoveFirst || isNotMoveLast) {
|
||
diffPosX = 0;
|
||
}
|
||
}
|
||
const value = (diffPosX / this.#state.width) * 100;
|
||
const transitionNoneClass = this.#state.prefix + this.constructor.#TRANSITION_NONE;
|
||
this.#state.elItems.classList.remove(transitionNoneClass);
|
||
if (value > this.constructor.#SWIPE_THRESHOLD) {
|
||
this.#state.direction = 'next';
|
||
let count = 0;
|
||
while (count <= Math.floor(Math.abs(value) - this.constructor.#SWIPE_THRESHOLD) / 100) {
|
||
this.#move();
|
||
count += 1;
|
||
}
|
||
} else if (value < -this.constructor.#SWIPE_THRESHOLD) {
|
||
this.#state.direction = 'prev';
|
||
let count = 0;
|
||
while (count <= Math.floor(Math.abs(value) - this.constructor.#SWIPE_THRESHOLD) / 100) {
|
||
this.#move();
|
||
count += 1;
|
||
}
|
||
} else {
|
||
this.#state.direction = 'none';
|
||
this.#move();
|
||
}
|
||
this.#state.isSwiping = false;
|
||
if (this.#config.loop) {
|
||
this.#autoplay();
|
||
}
|
||
this.#state.isBalancing = false;
|
||
}
|
||
|
||
#touchMove(e) {
|
||
if (!this.#state.isSwiping) {
|
||
return;
|
||
}
|
||
this.#state.isMoving = true;
|
||
const event = e.type.search('touch') === 0 ? e.changedTouches[0] : e;
|
||
let diffPosX = this.#state.swipeX - event.clientX;
|
||
const diffPosY = this.#state.swipeY - event.clientY;
|
||
const prevPosX = this.#state.prevPosX ? this.#state.prevPosX : event.clientX;
|
||
const direction = prevPosX > event.clientX ? 'next' : 'prev';
|
||
this.#state.prevPosX = event.clientX;
|
||
if (!this.#state.isTouchMoving) {
|
||
if (Math.abs(diffPosY) > Math.abs(diffPosX) || Math.abs(diffPosX) === 0) {
|
||
this.#state.isSwiping = false;
|
||
return;
|
||
}
|
||
this.#state.isTouchMoving = true;
|
||
}
|
||
e.preventDefault();
|
||
if (!this.#config.loop) {
|
||
const isNotMoveFirst = this.#state.activeItems[0] === 1 && diffPosX <= 0;
|
||
const isNotMoveLast = this.#state.activeItems[this.#state.activeItems.length - 1] && diffPosX >= 0;
|
||
if (isNotMoveFirst || isNotMoveLast) {
|
||
diffPosX /= 4;
|
||
}
|
||
}
|
||
const transitionNoneClass = this.#state.prefix + this.constructor.#TRANSITION_NONE;
|
||
this.#state.elItems.classList.add(transitionNoneClass);
|
||
const translate = this.#state.translate - diffPosX;
|
||
this.#state.elItems.style.transform = `translate3D(${translate}px, 0px, 0.1px)`;
|
||
if (this.#config.loop) {
|
||
this.#state.direction = diffPosX > 0 ? 'next' : 'prev';
|
||
this.#state.direction = direction;
|
||
window.requestAnimationFrame(() => {
|
||
this.#balanceItems(true);
|
||
});
|
||
}
|
||
}
|
||
|
||
#attachEvents() {
|
||
this.#state.events = {
|
||
click: [this.#state.el, this.#onClick.bind(this), true],
|
||
mouseenter: [this.#state.el, this.#onMouseEnter.bind(this), true],
|
||
mouseleave: [this.#state.el, this.#onMouseLeave.bind(this), true],
|
||
transitionstart: [this.#state.elItems, this.#onTransitionStart.bind(this), this.#config.loop],
|
||
transitionend: [this.#state.elItems, this.#onTransitionEnd.bind(this), this.#config.loop],
|
||
touchstart: [this.#state.el, this.#touchStart.bind(this), this.#config.swipe],
|
||
mousedown: [this.#state.el, this.#touchStart.bind(this), this.#config.swipe],
|
||
touchend: [document, this.#touchEnd.bind(this), this.#config.swipe],
|
||
mouseup: [document, this.#touchEnd.bind(this), this.#config.swipe],
|
||
touchmove: [this.#state.el, this.#touchMove.bind(this), this.#config.swipe],
|
||
mousemove: [this.#state.el, this.#touchMove.bind(this), this.#config.swipe],
|
||
dragstart: [this.#state.el, this.#onDragStart.bind(this), true],
|
||
visibilitychange: [document, this.#onVisibilityChange.bind(this), true]
|
||
};
|
||
Object.keys(this.#state.events).forEach((type) => {
|
||
if (this.#state.events[type][2]) {
|
||
const el = this.#state.events[type][0];
|
||
const fn = this.#state.events[type][1];
|
||
if (type === 'touchstart' || type === 'touchmove') {
|
||
const options = this.constructor.checkSupportPassiveEvents() ? { passive: false } : false;
|
||
el.addEventListener(type, fn, options);
|
||
} else {
|
||
el.addEventListener(type, fn);
|
||
}
|
||
}
|
||
});
|
||
this.#resizeObserver = new ResizeObserver((entries) => {
|
||
window.requestAnimationFrame(this.#reset.bind(this));
|
||
});
|
||
this.#resizeObserver.observe(this.#state.elWrapper);
|
||
}
|
||
|
||
#detachEvents() {
|
||
Object.keys(this.#state.events).forEach((type) => {
|
||
if (this.#state.events[type][2]) {
|
||
const el = this.#state.events[type][0];
|
||
const fn = this.#state.events[type][1];
|
||
el.removeEventListener(type, fn);
|
||
this.#resizeObserver.disconnect();
|
||
}
|
||
});
|
||
}
|
||
|
||
#autoplay(action) {
|
||
if (!this.#config.autoplay) {
|
||
return;
|
||
}
|
||
if (action === 'stop') {
|
||
clearInterval(this.#state.intervalId);
|
||
this.#state.intervalId = null;
|
||
return;
|
||
}
|
||
if (this.#state.intervalId === null) {
|
||
this.#state.intervalId = setInterval(() => {
|
||
this.#state.direction = this.#config.direction === 'prev' ? 'prev' : 'next';
|
||
this.#move();
|
||
}, this.#config.interval);
|
||
}
|
||
}
|
||
|
||
#balanceItems(once = false) {
|
||
if (!this.#state.isBalancing && !once) {
|
||
return;
|
||
}
|
||
const wrapperRect = this.#state.elWrapper.getBoundingClientRect();
|
||
const targetWidth = wrapperRect.width / this.#state.countActiveItems / 2;
|
||
const countItems = this.#state.elListItem.length;
|
||
if (this.#state.direction === 'next') {
|
||
const exItemRectRight = this.#state.exItemMin.getBoundingClientRect().right;
|
||
if (exItemRectRight < wrapperRect.left - targetWidth) {
|
||
const elFound = this.#state.els.find((item) => item.el === this.#state.exItemMin);
|
||
elFound.order = this.#state.exOrderMin + countItems;
|
||
const translate = this.#state.exTranslateMin + countItems * this.#state.width;
|
||
elFound.translate = translate;
|
||
this.#state.exItemMin.style.transform = `translate3D(${translate}px, 0px, 0.1px)`;
|
||
this.#updateExProperties();
|
||
}
|
||
} else {
|
||
const exItemRectLeft = this.#state.exItemMax.getBoundingClientRect().left;
|
||
if (exItemRectLeft > wrapperRect.right + targetWidth) {
|
||
const elFound = this.#state.els.find((item) => item.el === this.#state.exItemMax);
|
||
elFound.order = this.#state.exOrderMax - countItems;
|
||
const translate = this.#state.exTranslateMax - countItems * this.#state.width;
|
||
elFound.translate = translate;
|
||
this.#state.exItemMax.style.transform = `translate3D(${translate}px, 0px, 0.1px)`;
|
||
this.#updateExProperties();
|
||
}
|
||
}
|
||
if (!once) {
|
||
window.requestAnimationFrame(() => {
|
||
this.#balanceItems(false);
|
||
});
|
||
}
|
||
}
|
||
|
||
#updateClasses() {
|
||
const activeClass = this.#state.prefix + this.constructor.#EL_ITEM_ACTIVE;
|
||
this.#state.activeItems.forEach((item, index) => {
|
||
if (item) {
|
||
this.#state.elListItem[index].classList.add(activeClass);
|
||
} else {
|
||
this.#state.elListItem[index].classList.remove(activeClass);
|
||
}
|
||
const elListIndicators = this.#state.el.querySelectorAll(`.${this.#state.prefix}${this.constructor.#EL_INDICATOR}`);
|
||
if (elListIndicators.length && item) {
|
||
elListIndicators[index].classList.add(`${this.#state.prefix}${this.constructor.#EL_INDICATOR_ACTIVE}`);
|
||
} else if (elListIndicators.length && !item) {
|
||
elListIndicators[index].classList.remove(`${this.#state.prefix}${this.constructor.#EL_INDICATOR_ACTIVE}`);
|
||
}
|
||
});
|
||
}
|
||
|
||
#move() {
|
||
if (this.#state.direction === 'none') {
|
||
const transform = this.#state.translate;
|
||
this.#state.elItems.style.transform = `translate3D(${transform}px, 0px, 0.1px)`;
|
||
return;
|
||
}
|
||
const widthItem = this.#state.direction === 'next' ? -this.#state.width : this.#state.width;
|
||
const transform = this.#state.translate + widthItem;
|
||
if (!this.#config.loop) {
|
||
const limit = this.#state.width * (this.#state.elListItem.length - this.#state.countActiveItems);
|
||
if (transform < -limit || transform > 0) {
|
||
return;
|
||
}
|
||
if (this.#state.btnPrev) {
|
||
this.#state.btnPrev.classList.remove(this.#state.btnClassHide);
|
||
this.#state.btnNext.classList.remove(this.#state.btnClassHide);
|
||
}
|
||
if (this.#state.btnPrev && transform === -limit) {
|
||
this.#state.btnNext.classList.add(this.#state.btnClassHide);
|
||
} else if (this.#state.btnPrev && transform === 0) {
|
||
this.#state.btnPrev.classList.add(this.#state.btnClassHide);
|
||
}
|
||
}
|
||
if (this.#state.direction === 'next') {
|
||
this.#state.activeItems = [...this.#state.activeItems.slice(-1), ...this.#state.activeItems.slice(0, -1)];
|
||
} else {
|
||
this.#state.activeItems = [...this.#state.activeItems.slice(1), ...this.#state.activeItems.slice(0, 1)];
|
||
}
|
||
this.#updateClasses();
|
||
this.#state.translate = transform;
|
||
this.#state.elItems.style.transform = `translate3D(${transform}px, 0px, 0.1px)`;
|
||
}
|
||
|
||
#moveTo(index) {
|
||
const delta = this.#state.activeItems.reduce((acc, current, currentIndex) => {
|
||
const diff = current ? index - currentIndex : acc;
|
||
return Math.abs(diff) < Math.abs(acc) ? diff : acc;
|
||
}, this.#state.activeItems.length);
|
||
if (delta !== 0) {
|
||
this.#state.direction = delta > 0 ? 'next' : 'prev';
|
||
for (let i = 0; i < Math.abs(delta); i++) {
|
||
this.#move();
|
||
}
|
||
}
|
||
}
|
||
|
||
// приватный метод для выполнения первичной инициализации
|
||
#init() {
|
||
// состояние элементов
|
||
this.#state.els = [];
|
||
// текущее значение translate
|
||
this.#state.translate = 0;
|
||
// позиции активных элементов
|
||
this.#state.activeItems = [];
|
||
// состояние элементов
|
||
this.#state.isBalancing = false;
|
||
// получаем gap между слайдами
|
||
const gap = parseFloat(getComputedStyle(this.#state.elItems).gap) || 0;
|
||
// ширина одного слайда
|
||
this.#state.width = this.#state.elListItem[0].getBoundingClientRect().width + gap;
|
||
// ширина #EL_WRAPPER
|
||
const widthWrapper = this.#state.elWrapper.getBoundingClientRect().width;
|
||
// количество активных элементов
|
||
this.#state.countActiveItems = Math.round(widthWrapper / this.#state.width);
|
||
this.#state.elListItem.forEach((el, index) => {
|
||
el.style.transform = '';
|
||
this.#state.activeItems.push(index < this.#state.countActiveItems ? 1 : 0);
|
||
this.#state.els.push({
|
||
el, index, order: index, translate: 0
|
||
});
|
||
});
|
||
if (this.#state.countActiveItems === this.#state.elListItem.length) {
|
||
if (this.#state.btnPrev) {
|
||
this.#state.btnPrev.classList.add(this.#state.btnClassHide);
|
||
}
|
||
if (this.#state.btnNext) {
|
||
this.#state.btnNext.classList.add(this.#state.btnClassHide);
|
||
}
|
||
} else {
|
||
if (this.#state.btnPrev) {
|
||
this.#state.btnPrev.classList.remove(this.#state.btnClassHide);
|
||
}
|
||
if (this.#state.btnNext) {
|
||
this.#state.btnNext.classList.remove(this.#state.btnClassHide);
|
||
}
|
||
}
|
||
if (this.#config.loop) {
|
||
const lastIndex = this.#state.elListItem.length - 1;
|
||
const translate = -(lastIndex + 1) * this.#state.width;
|
||
this.#state.elListItem[lastIndex].style.transform = `translate3D(${translate}px, 0px, 0.1px)`;
|
||
this.#state.els[lastIndex].order = -1;
|
||
this.#state.els[lastIndex].translate = translate;
|
||
this.#updateExProperties();
|
||
} else if (this.#state.btnPrev) {
|
||
this.#state.btnPrev.classList.add(this.#state.btnClassHide);
|
||
}
|
||
this.#updateClasses();
|
||
this.#autoplay();
|
||
}
|
||
|
||
#reset() {
|
||
const transitionNoneClass = this.#state.prefix + this.constructor.#TRANSITION_NONE;
|
||
// получаем gap между слайдами
|
||
const gap = parseFloat(getComputedStyle(this.#state.elItems).gap) || 0;
|
||
// ширина одного слайда
|
||
const widthItem = this.#state.elListItem[0].getBoundingClientRect().width + gap;
|
||
const widthWrapper = this.#state.elWrapper.getBoundingClientRect().width;
|
||
const countActiveEls = Math.round(widthWrapper / widthItem);
|
||
if (widthItem === this.#state.width && countActiveEls === this.#state.countActiveItems) {
|
||
return;
|
||
}
|
||
this.#autoplay('stop');
|
||
this.#state.elItems.classList.add(transitionNoneClass);
|
||
this.#state.elItems.style.transform = 'translate3D(0px, 0px, 0.1px)';
|
||
this.#init();
|
||
window.requestAnimationFrame(() => {
|
||
this.#state.elItems.classList.remove(transitionNoneClass);
|
||
});
|
||
}
|
||
|
||
#updateExProperties() {
|
||
const els = this.#state.els.map((item) => item.el);
|
||
const orders = this.#state.els.map((item) => item.order);
|
||
this.#state.exOrderMin = Math.min(...orders);
|
||
this.#state.exOrderMax = Math.max(...orders);
|
||
const min = orders.indexOf(this.#state.exOrderMin);
|
||
const max = orders.indexOf(this.#state.exOrderMax);
|
||
this.#state.exItemMin = els[min];
|
||
this.#state.exItemMax = els[max];
|
||
this.#state.exTranslateMin = this.#state.els[min].translate;
|
||
this.#state.exTranslateMax = this.#state.els[max].translate;
|
||
}
|
||
}
|
||
|
||
ItcSlider.createInstances();
|