class HoverGallery {
constructor(option) {
this.mm = gsap.matchMedia();
this.selector = option.selector;
this.selectorWrapper = document.createElement('DIV');
this.selectorInner = document.createElement('DIV');
this.slides = gsap.utils.toArray(this.selector.querySelectorAll(':scope > *'));
//this.slides = gsap.utils.toArray(this.selector.children);
//this.slides = gsap.utils.toArray(this.selector.querySelectorAll('.product-gallery-item'));
//this.slides = this.selector.querySelectorAll('.product-gallery-item');
this.slidesLength = this.slides.length;
this.slideDuration = option.duration ? option.duration : 0.3;
this.resetHover = option.resetHover;
this.progressPerItem = 1 / (this.slidesLength - 1);
this.threshold = this.progressPerItem / 5;
this.snapProgress = gsap.utils.snap(this.progressPerItem)
this.slideWidth = 0;
this.totalWidth = 0;
this.currentIndex = 0;
window.addEventListener('resize', function (event) {
this.resizeSlider();
}.bind(this));
this.animation = gsap.to(this.slides, 1, {
xPercent: "-=" + (this.slidesLength - 1) * 100,
ease: "none",
paused: true
})
this.draggable = new Draggable(document.createElement('DIV'), {
trigger: this.selectorInner,
onPress: this.setOnPress,
onDrag: this.setOnDrag,
onRelease: this.animateSlides,
});
this.init( () => {
this.resizeSlider();
this.createHoverBlocks();
});
}
// Init gallery
init(el) {
const promises = [];
const { selector, selectorWrapper, selectorInner, slides, slidesLength } = this;
if (slidesLength > 1) {
slides[0].classList.add('active');
selector.classList.add('product-gallery-images');
const wrap = (el, wrapper, classNames = null, dots = false) => {
el.parentNode.insertBefore(wrapper, el);
if (classNames !== null) {
wrapper.classList.add(classNames);
}
wrapper.appendChild(el);
if (dots) {
this.createDots();
}
}
wrap(selector, selectorInner, 'product-gallery-inner', false);
wrap(selectorInner, selectorWrapper, 'product-gallery-wrapper', true);
}
Promise.all( promises ).then( () => {
el();
});
}
// Create gallery dots
createDots() {
const { slides, selectorWrapper } = this;
// Create dots wrapper
let dotsFragment = document.createDocumentFragment();
const dotsWrapper = document.createElement('DIV');
dotsWrapper.classList.add('product-gallery-dots');
// Create dots
for (var i = 0; i < slides.length; i++) {
let dot = dotsWrapper.appendChild(document.createElement('DIV'));
dot.classList.add('dot');
selectorWrapper.appendChild(dotsWrapper);
}
if (!dotsFragment) {
selectorWrapper.appendChild(dotsFragment);
}
dotsWrapper.querySelectorAll(':scope > *')[0].classList.add('active');
}
// Change active class
changeActiveClass(index) {
const dots = this.selectorWrapper.querySelectorAll('.product-gallery-dots .dot');
[...dots, ...this.slides].forEach((item) => {
item.classList.remove('active');
})
dots[index].classList.add('active');
this.slides[index].classList.add('active');
}
// Animation slides
animateSlides = () => {
gsap.to(this.animation, this.slideDuration, {
progress: this.snapProgressDirectional(
this.animation.progress(),
this.draggable.deltaX > 0 ? 1 : -1
),
overwrite: true,
onComplete: () => {
const slideProgress = Math.floor(this.animation.progress() * this.slidesLength);
if (this.currentIndex !== slideProgress) {
this.currentIndex = slideProgress === this.slidesLength ? slideProgress - 1 : slideProgress;
this.changeActiveClass(this.currentIndex);
}
}
})
}
// Draggable set on drag function
setOnDrag = () => {
let change = (this.draggable.startX - this.draggable.x) / this.totalWidth;
this.animation.progress(this.draggable.startProgress + change)
}
// Draggable set on press function
setOnPress = () => {
gsap.killTweensOf(this.animation);
this.draggable.startProgress = this.animation.progress();
}
// Snap progress directional
snapProgressDirectional(value, direction) {
let snapped = this.snapProgress(value);
if (direction < 0 && value - snapped > this.threshold) {
return snapped + this.progressPerItem;
} else if (direction > 0 && snapped - value > this.threshold) {
return snapped - this.progressPerItem;
}
return snapped;
}
// Hover gallery blocks for desktop
createHoverBlocks() {
const { slides, slidesLength, selectorWrapper } = this;
if (slidesLength > 1) {
// Create hover gallery wrapper
let blockFragment = document.createDocumentFragment();
const hoverGalleryWrapper = document.createElement('DIV');
hoverGalleryWrapper.classList.add('hover-gallery-wrapper');
// Create dots
for (var i = 0; i < slides.length; i++) {
let block = hoverGalleryWrapper.appendChild(document.createElement('DIV'));
block.classList.add('hover-block');
selectorWrapper.appendChild(hoverGalleryWrapper);
}
if (!blockFragment) {
selectorWrapper.appendChild(blockFragment);
}
hoverGalleryWrapper.querySelectorAll(':scope > *')[this.currentIndex].classList.add('active');
this.slideHoverBlocks(hoverGalleryWrapper);
}
}
slideHoverBlocks(wrapper) {
this.mm.add("(min-width: 1200px)", () => {
const blocks = wrapper.querySelectorAll('.hover-gallery-wrapper .hover-block');
const removeClass = () => {
blocks.forEach((block) => {
block.classList.remove('active');
})
}
const over = (e) => {
this.currentIndex = Array.prototype.slice.call(wrapper.children).indexOf( e.target );
removeClass();
e.target.classList.add('active');
this.changeActiveClass(this.currentIndex);
}
const leave = (e) => {
this.currentIndex = this.currentIndex;
removeClass();
wrapper.children[0].classList.add('active');
this.changeActiveClass(this.resetHover ? 0 : this.currentIndex);
}
wrapper.addEventListener('mouseover', (e) => {
over(e);
})
wrapper.addEventListener('mouseleave', (e) => {
leave(e);
})
});
}
// Resize slider width
resizeSlider() {
setTimeout(() => {
this.slideWidth = this.slides[0]?.offsetWidth;
this.totalWidth = this.slideWidth * this.slidesLength;
}, 50);
/* this.mm.add("(min-width: 1200px)", () => {
this.animation.progress(0);
//this.draggable.startProgress = this.animation.progress();
this.draggable.updateProgress = this.animation.progress(0);
this.animateSlides();
}); */
}
}