1gallery
Version:
a modern SEO friendly ejs web banner gallery for any kind of slides (photos, html...)
397 lines (360 loc) • 16.5 kB
JavaScript
async function sleep$1(ms){
return new Promise((resolve,reject)=>setTimeout(resolve,ms));
}
function docReady$1(fn) {
// see if DOM is already available
if (document.readyState === "complete" || document.readyState === "interactive") {
// call on next available tick
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
function tryPreventingDefault$1(e){
if(typeof e !== "undefined" && typeof e.preventDefault !== "undefined")e.preventDefault();
}
function init(){
if(typeof window !== "undefined"){
window.sleep = sleep$1;
window.docReady = docReady$1;
window.tryPreventingDefault = tryPreventingDefault$1;
}
}
init();
var init1GalleryCore = (galleryNode)=> {
const self = galleryNode;
self.updateCardsCount = () => self.cardsCount = self.querySelectorAll('.card').length;
self.rearrangeCardsAroundActiveCard = () => {
self.classList.remove('smooth');
const afterActive = self.querySelectorAll('.card.active ~ .card').length;
let toPush = Math.floor(self.cardsCount / 2) - afterActive;
if (toPush > 0) {
for (; toPush > 0; toPush--) self.push(self.shiftInPlace());
}
if (toPush < 0) {
for (; toPush < 0; toPush++) self.unshiftInPlace(self.pop());
}
self.classList.add('smooth');
};
self.activeCard = () => self.querySelector('.card.active');
self.firstCard = () => self.querySelectorAll('.card')[0];
self.lastCard = () => self.querySelectorAll('.card')[self.cardsCount - 1];
self.pop = function pop() {
const lastCard = self.lastCard();
self.slider.removeChild(lastCard);
self.cardsCount--;
return lastCard;
};
self.push = function push(card) {
self.slider.appendChild(card);
self.cardsCount++;
};
self.shift = function shift() {
const firstCard = self.firstCard();
self.slider.removeChild(firstCard);
self.cardsCount--;
return firstCard;
};
self.shiftInPlace = function shiftInPlace() {
const refCard = self.firstCard();
const cardFullWidth = refCard.nextElementSibling.offsetLeft - refCard.offsetLeft;
self.slider.style.left = `${self.slider.offsetLeft + cardFullWidth}px`;
const res = self.shift();
self.slider.offsetLeft; // to fix a update issue making transition to jump.
return res;
};
self.unshift = function unshift(card) {
self.slider.insertBefore(card, self.firstCard());
self.cardsCount++;
};
self.unshiftInPlace = function unshiftInPlace(card) {
self.unshift(card);
const refCard = self.firstCard();
const cardFullWidth = refCard.nextElementSibling.offsetLeft - refCard.offsetLeft;
self.slider.style.left = `${self.slider.offsetLeft - cardFullWidth}px`;
self.slider.offsetLeft; // to fix a update issue making transition to jump.
};
self.first = (e) => self.changeActiveCard(self.cardsOrdered[0]) && tryPreventingDefault(e);
self.prev = (e) => self.changeActiveCard(self.activeCard().previousElementSibling) && tryPreventingDefault(e);
self.next = (e) => self.changeActiveCard(self.activeCard().nextElementSibling) && tryPreventingDefault(e);
self.last = (e) => self.changeActiveCard(self.cardsOrdered[self.cardsCount - 1]) && tryPreventingDefault(e);
self.changeActiveCard = (newActiveCard) => {
const oldActiveCard = self.activeCard();
oldActiveCard.classList.remove('active');
newActiveCard.classList.add('active');
self.rearrangeCardsAroundActiveCard();
self.updateCardsCounter();
self.centerActiveCard();
return true;
};
self.initSlider = () => self.slider = self.querySelector('.slideArea');
self.initCardOrder = () => self.cardsOrdered = Array.from(self.querySelectorAll('.card'));
if (!self.activeCard()) self.firstCard().classList.add('active');
self.updateCardsCount();
self.initCardOrder();
self.initSlider();
self.rearrangeCardsAroundActiveCard();
};
function initSizer(galleryNode) {
const self = galleryNode;
self.updateSize = ()=> self.cardsOrdered.forEach(card=>card.updateSize());
self.cardsOrdered.forEach((card)=>initCard(card,self));
self.updateSize();
window.addEventListener('resize',()=>{
self.centerActiveCardInstantly(); // include updateSize
});
}
function initCard(card, galleryNode){
const self = card;
self.gallery = galleryNode;
self.updateSize = ()=>{
const cardContentNode = self.querySelector(".cardContent");
const galleryRatio = self.gallery.offsetWidth / self.gallery.offsetHeight;
const screenRatio = window.innerWidth / window.innerHeight;
if(self.classList.contains('cover')){
cardContentNode.style.width = `${self.gallery.offsetWidth}px`;
cardContentNode.style.height = `${self.gallery.offsetHeight}px`;
} else if(self.classList.contains('ratio')){
const ratioWH = cardContentNode.className.match(/ratio-([0-9]+)-([0-9]+)/);
const cardRatio = ratioWH[1]/ratioWH[2];
if(cardRatio>galleryRatio) {
cardContentNode.style.width = `${self.gallery.offsetWidth}px`;
cardContentNode.style.height = `${self.gallery.offsetWidth/cardRatio}px`;
} else {
cardContentNode.style.width = `${self.gallery.offsetHeight*cardRatio}px`;
cardContentNode.style.height = `${self.gallery.offsetHeight}px`;
}
} else { // auto
if(screenRatio>galleryRatio) {
cardContentNode.style.width = `${self.gallery.offsetWidth}px`;
cardContentNode.style.height = `${self.gallery.offsetWidth/screenRatio}px`;
} else {
cardContentNode.style.width = `${self.gallery.offsetHeight*screenRatio}px`;
cardContentNode.style.height = `${self.gallery.offsetHeight}px`;
}
}
};
}
function initScroll(galleryNode) {
const self = galleryNode;
self.initPrevNext = () => {
self.resizePrevNext();
self.querySelector('.prev').addEventListener('click', self.prev);
self.querySelector('.next').addEventListener('click', self.next);
window.addEventListener('resize',self.resizePrevNext);
// mobile swipe https://css-tricks.com/simple-swipe-with-vanilla-javascript/
function unify(e) { return e.changedTouches ? e.changedTouches[0] : e } let initialX = "not dragging";
let initialSliderX;
async function press(e){
tryPreventingDefault(e);
self.classList.remove('smooth');
await sleep(0);
initialX = unify(e).clientX;
initialSliderX = self.slider.offsetLeft;
}
function drag(e){
if("not dragging" === initialX) return;
tryPreventingDefault(e);
self.slider.style.left = `${Math.round(initialSliderX + unify(e).clientX - initialX)}px`;
}
async function release(e){
const actualX = unify(e).clientX;
self.classList.add('smooth');
if (actualX-initialX > window.innerWidth/20){
self.prev(unify(e));
} else if (initialX-actualX > window.innerWidth/20){
self.next(unify(e));
} else {
self.centerActiveCard();
}
initialX = "not dragging";
}
self.slider.addEventListener('mousedown', press);
self.slider.addEventListener('touchstart', press);
self.slider.addEventListener('mousemove', drag);
self.slider.addEventListener('touchmove', drag);
self.slider.addEventListener('mouseup', release);
self.slider.addEventListener('touchend', release);
self.slider.addEventListener('touchcancel',release);
self.uglySwipZoomFirefoxFix = release;
//self.slider.addEventListener('mouseleave',release); // déplacé dans zoom.mjs en conditionnel pour éviter les conflit au dézoom via esc dans firefox. (gère le glissé hors zone de galerie).
//self.slider.addEventListener('touchleave',release);
};
self.resizePrevNext = ()=>{
const resizeButton = btn=>{
btn.style.height = `${btn.offsetWidth}px`;
btn.style.marginTop = `-${btn.offsetWidth/2}px`;
};
resizeButton(self.querySelector('.prev'));
resizeButton(self.querySelector('.next'));
};
self.initCardCounter = () => {
self.cardCounter = self.querySelector('.cardCounter');
if (self.cardsCount < 10) {
self.cardCounter.classList.add('dots');
const dots = [];
for (let i = 0; i < self.cardsCount; i++) dots.push(`<a class="dot" href="#card-${i + 1}"></a>`); // ⬤
self.cardCounter.innerHTML = dots.join('');
Array.from(self.cardCounter.children).forEach((n, i) => {
n.addEventListener('click', (e) => self.changeActiveCard(self.cardsOrdered[i]) && tryPreventingDefault(e));
});
} else {
self.cardCounter.classList.add('arrowNav');
self.cardCounter.innerHTML = `
<a class="first" href="#firstCard">«</a>
<a class="prev" href="#previousCard">‹</a>
<span class="currentCard"></span>
/
<span class="totalCards">${self.cardsCount}</span>
<a class="next" href="#nextCard">›</a>
<a class="last" href="#lastCard">»</a>
`;
const onDo = (selector, action) => self.cardCounter.querySelector(selector).addEventListener('click', action);
onDo('.first', self.first);
onDo('.prev', self.prev);
onDo('.next', self.next);
onDo('.last', self.last);
}
self.updateCardsCounter();
};
self.updateCardsCounter = () => {
const activeNumber = self.cardsOrdered.indexOf(self.activeCard());
if (self.cardsCount < 10) {
const oldActive = self.cardCounter.querySelector('.active');
if (oldActive) oldActive.classList.remove('active');
self.cardCounter.children[activeNumber].classList.add('active');
} else self.cardCounter.querySelector('.currentCard').innerHTML = `${activeNumber + 1}`;
};
self.initPrevNext();
self.initCardCounter();
}
var initScroll$1 = async (galleryNode) => {
const self = galleryNode;
self.centerActiveCard = () => {
if(typeof self.updateSize !== "undefined") self.updateSize();
const galleryWidth = self.offsetWidth;
const activeCardWidth = self.activeCard().offsetWidth;
const activeCardLeft = self.activeCard().offsetLeft;
self.slider.style.left = `${galleryWidth / 2 - activeCardLeft - activeCardWidth / 2}px`;
};
self.centerActiveCardInstantly = async () => {
self.classList.remove('smooth');
await sleep(0);
self.classList.remove('smooth');
self.centerActiveCard();
await sleep(0);
self.classList.add('smooth');
};
await self.centerActiveCardInstantly();
};
var initAutoPlay = (galleryNode) => {
const self = galleryNode;
self.initAutoPlay = ()=>{
const isAutoPlay = self.className.match(/autoplay-([0-9]+)s/);
if(!isAutoPlay) return;
const playSpeed = parseInt(isAutoPlay[1]);
setInterval(self.autoNext,playSpeed*1000);
const setPauseArea = (pauseArea)=>{
pauseArea.addEventListener('mouseenter', ()=>self.pause=true);
pauseArea.addEventListener('mouseleave', ()=>self.pause=false);
};
setPauseArea(self.cardCounter);
setPauseArea(self.querySelector('.prev'));
setPauseArea(self.querySelector('.next'));
// Don't auto-play when dragging / swapping
self.slider.addEventListener('mousedown', ()=>self.pause=true);
self.slider.addEventListener('touchstart', ()=>self.pause=true);
self.slider.addEventListener('mouseup', ()=>self.pause=false);
self.slider.addEventListener('touchend', ()=>self.pause=false);
self.slider.addEventListener('touchcancel',()=>self.pause=false);
};
self.autoNext = ()=>{
if(self.pause) return;
self.next();
};
self.initAutoPlay();
};
function init$1(galleryNode){
const self = galleryNode;
self.zoomToggle = async (e)=>{
tryPreventingDefault(e);
if(self.classList.contains('fullScreen')) await self.zoomOut();
else await self.zoomFullScreen();
return false;
};
self.zoomFullScreen = async ()=>{
self.slider.removeEventListener('mouseleave',self.uglySwipZoomFirefoxFix);
self.classList.add('fullScreen');
try{await self.requestFullscreen();}catch (e){console.error(e);}
self.activateKeyboardNav();
self.resizePrevNext();
await self.centerActiveCardInstantly();
// to try to mitigate firefox resize/fullscreen random behavior and false size delay
await sleep(100);
await self.centerActiveCardInstantly();
};
self.zoomOut = async ()=>{
self.classList.remove('fullScreen');
try{await document.exitFullscreen();}catch (e){}
self.disableKeyboardNav();
await sleep(0);
self.resizePrevNext();
await self.centerActiveCardInstantly();
// to try to mitigate firefox resize/fullscreen random behavior and false size delay
await sleep(100);
await self.centerActiveCardInstantly();
self.slider.addEventListener('mouseleave',self.uglySwipZoomFirefoxFix);
};
self.keyUpFunc = (event)=>{
switch (event.code){
case "ArrowLeft" : self.prev(); break;
case "Space" : case "ArrowRight" : self.next(); break;
case "PageUp" : case "Home" : self.first(); break;
case "PageDown" : case "End" : self.last(); break;
case "Digit1" : if(self.cardsOrdered[0]) self.changeActiveCard(self.cardsOrdered[0]); else return; break;
case "Digit2" : if(self.cardsOrdered[1]) self.changeActiveCard(self.cardsOrdered[1]); else return; break;
case "Digit3" : if(self.cardsOrdered[2]) self.changeActiveCard(self.cardsOrdered[2]); else return; break;
case "Digit4" : if(self.cardsOrdered[3]) self.changeActiveCard(self.cardsOrdered[3]); else return; break;
case "Digit5" : if(self.cardsOrdered[4]) self.changeActiveCard(self.cardsOrdered[4]); else return; break;
case "Digit6" : if(self.cardsOrdered[5]) self.changeActiveCard(self.cardsOrdered[5]); else return; break;
case "Digit7" : if(self.cardsOrdered[6]) self.changeActiveCard(self.cardsOrdered[6]); else return; break;
case "Digit8" : if(self.cardsOrdered[7]) self.changeActiveCard(self.cardsOrdered[7]); else return; break;
case "Digit9" : if(self.cardsOrdered[8]) self.changeActiveCard(self.cardsOrdered[8]); else return; break;
case "Digit0" : if(self.cardsOrdered[9]) self.changeActiveCard(self.cardsOrdered[9]); else return; break;
default:
return;
}
tryPreventingDefault(event);
};
self.activateKeyboardNav = ()=>{
document.addEventListener('keyup', self.keyUpFunc);
};
self.disableKeyboardNav = ()=>{
document.removeEventListener('keyup',self.keyUpFunc);
};
if(!self.classList.contains('zoomable')) return;
const zoomButton = document.createElement('a');
zoomButton.classList.add('zoom');
zoomButton.setAttribute('href','#fullScreen');
zoomButton.innerHTML = `<span>🔍</span>`;
zoomButton.addEventListener('click', self.zoomToggle);
self.appendChild(zoomButton);
document.addEventListener('fullscreenchange', async ()=> {
if (!document.fullscreenElement && self.classList.contains('fullScreen')) await self.zoomOut();
});
}
function init$2(){
init();
document.querySelectorAll('._1gallery').forEach(g=>{
if(g.classList.contains('js')) return;
g.classList.add('js');
init1GalleryCore(g);
initScroll(g);
initSizer(g);
initScroll$1(g);
initAutoPlay(g);
init$1(g);
});
}
docReady(init$2);
export default init$2;