feat(lightbox): 接入全站图片预览
This commit is contained in:
1 parent
0985d0cb0a
commit
ddf7c37556
29 files changed
+1212
-1805
No files matched your search
+354
-255
@@ -9,10 +9,6 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* ============================================================
|
||||
* 1. 图片灯箱 — zoom / thumbnails / slideshow / fullscreen /
|
||||
* mousewheel / origin animation / smart download
|
||||
* ============================================================ */
|
||||
var lightboxEl = null;
|
||||
var lightboxImg = null;
|
||||
var lightboxCaption = null;
|
||||
@@ -23,63 +19,111 @@
|
||||
var currentZoom = 1;
|
||||
var autoplayTimer = null;
|
||||
var isAutoplay = false;
|
||||
var EXCLUDE_SEL = '.no-imgbox,.no-lightbox,.avatar,.img-icon,.avatar-badge,.aksrt-avatar';
|
||||
var TRIGGER_SEL = '.entry-content img, .wp-posts-content img, .aksrt-lightbox-trigger img, [data-aksrt-lightbox] img, .imgbox-container img, .comment-content .box-img img, a[data-imgbox] img';
|
||||
var CONTAINER_SEL = '.entry-content, .wp-posts-content, .aksrt-lightbox-trigger, [data-aksrt-lightbox], .imgbox-container, .comment-content';
|
||||
var lastTrigger = null;
|
||||
var previousBodyOverflow = '';
|
||||
var EXCLUDE_SEL = '.no-imgbox,.no-lightbox,.avatar,.img-icon,.avatar-badge,.aksrt-avatar,[data-no-lightbox],[data-aksrt-lightbox="0"]';
|
||||
var TRIGGER_SEL = '[data-aksrt-lightbox="1"] img,.aksrt-lightbox-trigger img,.imgbox-container img,.comment-content .box-img';
|
||||
var ANCHOR_TRIGGER_SEL = 'a[data-aksrt-lightbox-trigger]';
|
||||
var GROUP_SEL = '[data-aksrt-lightbox-group],.content-gallery[data-aksrt-lightbox="1"],.aksrt-carousel-block[data-aksrt-lightbox="1"],.imgbox-container,.comment-content,.post_front_content[data-aksrt-lightbox="1"],.wp-posts-content[data-aksrt-lightbox="1"],.entry-thumb[data-aksrt-lightbox="1"],.entry-content[data-aksrt-lightbox="1"],.aksrt-lightbox-trigger,[data-aksrt-lightbox="1"]';
|
||||
var IMAGE_URL_RE = /\.(?:avif|bmp|gif|ico|jpe?g|png|svg|tiff?|webp)(?:[?#].*)?$/i;
|
||||
var MAX_ZOOM_DESKTOP = 2;
|
||||
var MAX_ZOOM_MOBILE = 3;
|
||||
|
||||
function lightboxFlag(key, fallback) {
|
||||
var w = window._win || (window.aksrtData && window.aksrtData.imgbox) || {};
|
||||
if (w[key] === undefined || w[key] === null || w[key] === '') return fallback;
|
||||
return w[key] === true || w[key] === 1 || w[key] === '1';
|
||||
}
|
||||
function lightboxEnabled() {
|
||||
return lightboxFlag('imgbox', true);
|
||||
var config = window._win || {};
|
||||
if (config[key] === undefined || config[key] === null || config[key] === '') return fallback;
|
||||
return config[key] === true || config[key] === 1 || config[key] === '1';
|
||||
}
|
||||
|
||||
function lightboxEnabled() {
|
||||
return lightboxFlag('imgbox', false);
|
||||
}
|
||||
|
||||
function normalizeFullUrl(url) {
|
||||
if (!url || url.indexOf('data:') === 0) return url || '';
|
||||
return url
|
||||
.replace(/-\d+x\d+(\.[a-z0-9]+)(?=([?#]|$))/i, '$1')
|
||||
.replace(/([?&])x-oss-process=[^#&]*&?/i, '$1')
|
||||
.replace(/[?&]$/, '');
|
||||
}
|
||||
|
||||
function resolveFullUrl(img) {
|
||||
var full = img.getAttribute('data-full-url') || img.getAttribute('data-src') || '';
|
||||
if (full) return full;
|
||||
var src = img.src || '';
|
||||
// thumbnail → original: strip -NNNxNNN suffix
|
||||
return src.replace(/-\d+x\d+(\.[a-z]+)$/i, '$1');
|
||||
var explicitFull = img.getAttribute('data-full-url') || '';
|
||||
if (explicitFull) return normalizeFullUrl(explicitFull);
|
||||
|
||||
var anchor = img.closest('a[href]');
|
||||
var href = anchor ? anchor.getAttribute('href') || '' : '';
|
||||
if (IMAGE_URL_RE.test(href)) return normalizeFullUrl(href);
|
||||
|
||||
return normalizeFullUrl(
|
||||
img.getAttribute('data-src')
|
||||
|| img.getAttribute('data-lazy-src')
|
||||
|| img.currentSrc
|
||||
|| img.getAttribute('src')
|
||||
|| ''
|
||||
);
|
||||
}
|
||||
|
||||
function resolveThumbUrl(img) {
|
||||
return img.currentSrc
|
||||
|| img.getAttribute('src')
|
||||
|| img.getAttribute('data-src')
|
||||
|| img.getAttribute('data-lazy-src')
|
||||
|| img.getAttribute('data-full-url')
|
||||
|| '';
|
||||
}
|
||||
|
||||
function resolveCaption(img) {
|
||||
var anchor = img.closest('a');
|
||||
var explicit = anchor ? anchor.getAttribute('data-caption') || '' : '';
|
||||
if (explicit.trim()) return explicit.trim();
|
||||
|
||||
var figure = img.closest('figure');
|
||||
var figcaption = figure ? figure.querySelector('figcaption') : null;
|
||||
if (figcaption && figcaption.textContent.trim()) return figcaption.textContent.trim();
|
||||
|
||||
return (img.getAttribute('alt') || '').trim();
|
||||
}
|
||||
|
||||
function isNoLightbox(img) {
|
||||
if (img.closest(EXCLUDE_SEL)) return true;
|
||||
// parent anchor that does NOT point to an image
|
||||
var parentA = img.closest('a');
|
||||
if (!img || img.closest(EXCLUDE_SEL)) return true;
|
||||
|
||||
var parentA = img.closest('a[href]');
|
||||
if (parentA) {
|
||||
var href = parentA.getAttribute('href') || '';
|
||||
if (href && !/\.(jpe?g|png|gif|webp|avif|svg|bmp)(\?|$)/i.test(href) && !parentA.hasAttribute('data-imgbox')) {
|
||||
if (href && !IMAGE_URL_RE.test(href) && !parentA.hasAttribute('data-aksrt-lightbox-trigger')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
return resolveFullUrl(img) === '';
|
||||
}
|
||||
|
||||
function createLightbox() {
|
||||
if (!lightboxEnabled()) return;
|
||||
if (lightboxEl) return;
|
||||
if (!lightboxEnabled()) return null;
|
||||
if (lightboxEl) return lightboxEl;
|
||||
|
||||
lightboxEl = document.createElement('div');
|
||||
lightboxEl.className = 'aksrt-lightbox';
|
||||
lightboxEl.setAttribute('role', 'dialog');
|
||||
lightboxEl.setAttribute('aria-modal', 'true');
|
||||
lightboxEl.setAttribute('aria-hidden', 'true');
|
||||
lightboxEl.setAttribute('aria-label', '图片预览');
|
||||
lightboxEl.setAttribute('tabindex', '-1');
|
||||
lightboxEl.innerHTML =
|
||||
'<div class="aksrt-lightbox__toolbar">' +
|
||||
'<span class="aksrt-lightbox__counter"></span>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool toggle-zoom" title="缩放"><i class="fa fas fa-search-plus"></i></button>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool toggle-thumbs" title="缩略图"><i class="fa fas fa-th"></i></button>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool toggle-play" title="幻灯片"><i class="fa fas fa-play"></i></button>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool toggle-full" title="全屏"><i class="fa fas fa-expand"></i></button>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool aksrt-lightbox__download" title="下载"><i class="fa fas fa-download"></i></button>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool aksrt-lightbox__close" title="关闭"><i class="fa fas fa-times"></i></button>' +
|
||||
'<span class="aksrt-lightbox__counter" aria-live="polite"></span>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool toggle-zoom" title="缩放" aria-label="缩放"><i class="fa fas fa-search-plus" aria-hidden="true"></i></button>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool toggle-thumbs" title="缩略图" aria-label="显示或隐藏缩略图"><i class="fa fas fa-th" aria-hidden="true"></i></button>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool toggle-play" title="幻灯片" aria-label="播放幻灯片"><i class="fa fas fa-play" aria-hidden="true"></i></button>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool toggle-full" title="全屏" aria-label="切换全屏"><i class="fa fas fa-expand" aria-hidden="true"></i></button>' +
|
||||
'<a class="aksrt-lightbox__tool aksrt-lightbox__download" title="下载" aria-label="下载图片" download><i class="fa fas fa-download" aria-hidden="true"></i></a>' +
|
||||
'<button type="button" class="aksrt-lightbox__tool aksrt-lightbox__close" title="关闭" aria-label="关闭"><i class="fa fas fa-times" aria-hidden="true"></i></button>' +
|
||||
'</div>' +
|
||||
'<button type="button" class="aksrt-lightbox__nav aksrt-lightbox__nav--prev" aria-label="上一张"><i class="fa fas fa-chevron-left"></i></button>' +
|
||||
'<button type="button" class="aksrt-lightbox__nav aksrt-lightbox__nav--prev" aria-label="上一张"><i class="fa fas fa-chevron-left" aria-hidden="true"></i></button>' +
|
||||
'<div class="aksrt-lightbox__stage"><img class="aksrt-lightbox__img" src="" alt=""></div>' +
|
||||
'<button type="button" class="aksrt-lightbox__nav aksrt-lightbox__nav--next" aria-label="下一张"><i class="fa fas fa-chevron-right"></i></button>' +
|
||||
'<div class="aksrt-lightbox__caption"></div>' +
|
||||
'<button type="button" class="aksrt-lightbox__nav aksrt-lightbox__nav--next" aria-label="下一张"><i class="fa fas fa-chevron-right" aria-hidden="true"></i></button>' +
|
||||
'<div class="aksrt-lightbox__caption" aria-live="polite"></div>' +
|
||||
'<div class="aksrt-lightbox__thumbs" hidden></div>';
|
||||
|
||||
document.body.appendChild(lightboxEl);
|
||||
@@ -89,223 +133,260 @@
|
||||
lightboxCounter = lightboxEl.querySelector('.aksrt-lightbox__counter');
|
||||
lightboxThumbsWrap = lightboxEl.querySelector('.aksrt-lightbox__thumbs');
|
||||
|
||||
// Apply window._win imgbox feature flags
|
||||
var zoomBtn = lightboxEl.querySelector('.toggle-zoom');
|
||||
var thumbsBtn = lightboxEl.querySelector('.toggle-thumbs');
|
||||
var playBtn = lightboxEl.querySelector('.toggle-play');
|
||||
var fullBtn = lightboxEl.querySelector('.toggle-full');
|
||||
var downBtn = lightboxEl.querySelector('.aksrt-lightbox__download');
|
||||
if (zoomBtn) zoomBtn.hidden = !lightboxFlag('imgbox_zoom', true);
|
||||
if (thumbsBtn) thumbsBtn.hidden = !lightboxFlag('imgbox_thumbs', true);
|
||||
if (playBtn) playBtn.hidden = !lightboxFlag('imgbox_play', false);
|
||||
if (fullBtn) fullBtn.hidden = !lightboxFlag('imgbox_full', true);
|
||||
if (downBtn) downBtn.hidden = !lightboxFlag('imgbox_down', false);
|
||||
|
||||
// Close
|
||||
lightboxEl.querySelector('.aksrt-lightbox__close').addEventListener('click', function (e) { e.stopPropagation(); closeLightbox(); });
|
||||
lightboxEl.addEventListener('click', function (e) { if (e.target === lightboxEl || e.target.classList.contains('aksrt-lightbox__stage')) closeLightbox(); });
|
||||
|
||||
// Nav
|
||||
lightboxEl.querySelector('.aksrt-lightbox__nav--prev').addEventListener('click', function (e) { e.stopPropagation(); navigateLightbox(-1); });
|
||||
lightboxEl.querySelector('.aksrt-lightbox__nav--next').addEventListener('click', function (e) { e.stopPropagation(); navigateLightbox(1); });
|
||||
|
||||
// Keyboard
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (!lightboxEl.classList.contains('is-open')) return;
|
||||
if (e.key === 'Escape') closeLightbox();
|
||||
if (e.key === 'ArrowLeft') navigateLightbox(-1);
|
||||
if (e.key === 'ArrowRight') navigateLightbox(1);
|
||||
lightboxEl.querySelector('.aksrt-lightbox__close').addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
closeLightbox();
|
||||
});
|
||||
lightboxEl.addEventListener('click', function (event) {
|
||||
if (event.target === lightboxEl || event.target.classList.contains('aksrt-lightbox__stage')) closeLightbox();
|
||||
});
|
||||
lightboxEl.querySelector('.aksrt-lightbox__nav--prev').addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
navigateLightbox(-1);
|
||||
});
|
||||
lightboxEl.querySelector('.aksrt-lightbox__nav--next').addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
navigateLightbox(1);
|
||||
});
|
||||
|
||||
// Mousewheel nav
|
||||
lightboxEl.addEventListener('wheel', function (e) {
|
||||
if (!lightboxEl.classList.contains('is-open')) return;
|
||||
if (currentZoom > 1) return; // don't nav when zoomed
|
||||
e.preventDefault();
|
||||
if (e.deltaY > 0) navigateLightbox(1);
|
||||
else navigateLightbox(-1);
|
||||
document.addEventListener('keydown', function (event) {
|
||||
if (!lightboxEl || !lightboxEl.classList.contains('is-open')) return;
|
||||
if (event.key === 'Escape') closeLightbox();
|
||||
if (event.key === 'ArrowLeft') navigateLightbox(-1);
|
||||
if (event.key === 'ArrowRight') navigateLightbox(1);
|
||||
if (event.key === 'Tab') trapFocus(event);
|
||||
});
|
||||
|
||||
lightboxEl.addEventListener('wheel', function (event) {
|
||||
if (!lightboxEl.classList.contains('is-open') || currentZoom > 1 || currentGallery.length < 2) return;
|
||||
event.preventDefault();
|
||||
navigateLightbox(event.deltaY > 0 ? 1 : -1);
|
||||
}, { passive: false });
|
||||
|
||||
// Image click → toggle zoom
|
||||
lightboxImg.addEventListener('click', function (e) { e.stopPropagation(); toggleZoom(); });
|
||||
|
||||
// Double-click → zoom in
|
||||
lightboxImg.addEventListener('dblclick', function (e) {
|
||||
e.stopPropagation();
|
||||
lightboxImg.addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
if (lightboxFlag('imgbox_zoom', true)) toggleZoom();
|
||||
});
|
||||
lightboxImg.addEventListener('dblclick', function (event) {
|
||||
if (!lightboxFlag('imgbox_zoom', true)) return;
|
||||
event.stopPropagation();
|
||||
var maxZoom = window.innerWidth < 768 ? MAX_ZOOM_MOBILE : MAX_ZOOM_DESKTOP;
|
||||
if (currentZoom < maxZoom) setZoom(maxZoom);
|
||||
else setZoom(1);
|
||||
setZoom(currentZoom < maxZoom ? maxZoom : 1);
|
||||
});
|
||||
|
||||
// Pinch zoom (mobile)
|
||||
bindTouchGestures();
|
||||
|
||||
lightboxEl.querySelector('.toggle-zoom').addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
toggleZoom();
|
||||
});
|
||||
lightboxEl.querySelector('.toggle-thumbs').addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
lightboxThumbsWrap.hidden = !lightboxThumbsWrap.hidden;
|
||||
});
|
||||
lightboxEl.querySelector('.toggle-play').addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
toggleAutoplay();
|
||||
});
|
||||
lightboxEl.querySelector('.toggle-full').addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
toggleFullscreen();
|
||||
});
|
||||
|
||||
document.addEventListener('fullscreenchange', syncFullscreenState);
|
||||
document.addEventListener('webkitfullscreenchange', syncFullscreenState);
|
||||
|
||||
return lightboxEl;
|
||||
}
|
||||
|
||||
function bindTouchGestures() {
|
||||
var pinchStart = 0;
|
||||
lightboxEl.addEventListener('touchstart', function (e) {
|
||||
if (e.touches.length === 2) {
|
||||
var touchStartY = 0;
|
||||
var touchStartX = 0;
|
||||
var pullDistance = 0;
|
||||
|
||||
lightboxEl.addEventListener('touchstart', function (event) {
|
||||
if (event.touches.length === 2 && lightboxFlag('imgbox_zoom', true)) {
|
||||
pinchStart = Math.hypot(
|
||||
e.touches[1].clientX - e.touches[0].clientX,
|
||||
e.touches[1].clientY - e.touches[0].clientY
|
||||
event.touches[1].clientX - event.touches[0].clientX,
|
||||
event.touches[1].clientY - event.touches[0].clientY
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (event.touches.length === 1 && currentZoom <= 1.05) {
|
||||
touchStartY = event.touches[0].clientY;
|
||||
touchStartX = event.touches[0].clientX;
|
||||
pullDistance = 0;
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
lightboxEl.addEventListener('touchmove', function (e) {
|
||||
if (e.touches.length === 2 && pinchStart > 0) {
|
||||
var dist = Math.hypot(
|
||||
e.touches[1].clientX - e.touches[0].clientX,
|
||||
e.touches[1].clientY - e.touches[0].clientY
|
||||
lightboxEl.addEventListener('touchmove', function (event) {
|
||||
if (event.touches.length === 2 && pinchStart > 0 && lightboxFlag('imgbox_zoom', true)) {
|
||||
var distance = Math.hypot(
|
||||
event.touches[1].clientX - event.touches[0].clientX,
|
||||
event.touches[1].clientY - event.touches[0].clientY
|
||||
);
|
||||
var maxZoom = window.innerWidth < 768 ? MAX_ZOOM_MOBILE : MAX_ZOOM_DESKTOP;
|
||||
var newZoom = Math.min(maxZoom, Math.max(1, currentZoom * (dist / pinchStart)));
|
||||
setZoom(newZoom);
|
||||
pinchStart = dist;
|
||||
setZoom(Math.min(maxZoom, Math.max(1, currentZoom * (distance / pinchStart))));
|
||||
pinchStart = distance;
|
||||
return;
|
||||
}
|
||||
}, { passive: true });
|
||||
if (event.touches.length !== 1 || currentZoom > 1.05) return;
|
||||
|
||||
lightboxEl.addEventListener('touchend', function () { pinchStart = 0; }, { passive: true });
|
||||
|
||||
// Touch swipe/pull-dismiss (skip when zoomed)
|
||||
var touchStartY = 0, touchStartX = 0, isSwiping = false;
|
||||
lightboxEl.addEventListener('touchstart', function (e) {
|
||||
if (e.touches.length !== 1 || currentZoom > 1.05) return;
|
||||
touchStartY = e.touches[0].clientY;
|
||||
touchStartX = e.touches[0].clientX;
|
||||
isSwiping = false;
|
||||
}, { passive: true });
|
||||
|
||||
lightboxEl.addEventListener('touchmove', function (e) {
|
||||
if (e.touches.length !== 1 || currentZoom > 1.05) return;
|
||||
var dy = e.touches[0].clientY - touchStartY;
|
||||
var dx = e.touches[0].clientX - touchStartX;
|
||||
var dy = event.touches[0].clientY - touchStartY;
|
||||
var dx = event.touches[0].clientX - touchStartX;
|
||||
if (Math.abs(dy) > Math.abs(dx) && dy > 30) {
|
||||
isSwiping = true;
|
||||
var opacity = Math.max(0.2, (150 - dy) / 80);
|
||||
pullDistance = dy;
|
||||
lightboxImg.style.transform = 'translate3d(0, ' + dy + 'px, 0) scale(' + currentZoom + ')';
|
||||
lightboxImg.style.opacity = String(Math.min(1, opacity));
|
||||
lightboxImg.style.opacity = String(Math.max(0.2, Math.min(1, (150 - dy) / 80)));
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
lightboxEl.addEventListener('touchend', function () {
|
||||
if (isSwiping) {
|
||||
pinchStart = 0;
|
||||
if (pullDistance > 100) {
|
||||
closeLightbox();
|
||||
lightboxImg.style.transform = '';
|
||||
lightboxImg.style.opacity = '';
|
||||
} else if (pullDistance > 0) {
|
||||
setZoom(1);
|
||||
}
|
||||
isSwiping = false;
|
||||
lightboxImg.style.opacity = '';
|
||||
pullDistance = 0;
|
||||
}, { passive: true });
|
||||
|
||||
// Download
|
||||
lightboxEl.querySelector('.aksrt-lightbox__download').addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
var item = currentGallery[currentIndex];
|
||||
if (!item || !item.src) return;
|
||||
var a = document.createElement('a');
|
||||
var fullSrc = item.fullSrc || item.src;
|
||||
a.href = fullSrc;
|
||||
a.download = fullSrc.substring(fullSrc.lastIndexOf('/') + 1) || 'image';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
});
|
||||
|
||||
// Zoom button
|
||||
lightboxEl.querySelector('.toggle-zoom').addEventListener('click', function (e) { e.stopPropagation(); toggleZoom(); });
|
||||
|
||||
// Thumbnails toggle
|
||||
lightboxEl.querySelector('.toggle-thumbs').addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
lightboxThumbsWrap.hidden = !lightboxThumbsWrap.hidden;
|
||||
});
|
||||
|
||||
// Slideshow toggle
|
||||
lightboxEl.querySelector('.toggle-play').addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
toggleAutoplay();
|
||||
});
|
||||
|
||||
// Fullscreen
|
||||
lightboxEl.querySelector('.toggle-full').addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
toggleFullscreen();
|
||||
});
|
||||
|
||||
document.addEventListener('fullscreenchange', function () {
|
||||
var icon = lightboxEl.querySelector('.toggle-full i');
|
||||
if (document.fullscreenElement) {
|
||||
lightboxEl.classList.add('full-screen');
|
||||
icon.className = 'fa fas fa-compress';
|
||||
} else {
|
||||
lightboxEl.classList.remove('full-screen');
|
||||
icon.className = 'fa fas fa-expand';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setZoom(z) {
|
||||
currentZoom = z;
|
||||
lightboxImg.style.transform = 'scale(' + z + ')';
|
||||
lightboxEl.classList.toggle('is-zoomed', z > 1);
|
||||
function trapFocus(event) {
|
||||
var focusable = Array.prototype.filter.call(
|
||||
lightboxEl.querySelectorAll('a[href],button:not([hidden]):not([disabled]),[tabindex]:not([tabindex="-1"])'),
|
||||
function (element) { return element.offsetParent !== null; }
|
||||
);
|
||||
if (!focusable.length) {
|
||||
event.preventDefault();
|
||||
lightboxEl.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
var first = focusable[0];
|
||||
var last = focusable[focusable.length - 1];
|
||||
if (event.shiftKey && document.activeElement === first) {
|
||||
event.preventDefault();
|
||||
last.focus();
|
||||
} else if (!event.shiftKey && document.activeElement === last) {
|
||||
event.preventDefault();
|
||||
first.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function setZoom(zoom) {
|
||||
if (!lightboxImg || !lightboxEl) return;
|
||||
currentZoom = zoom;
|
||||
lightboxImg.style.transform = 'scale(' + zoom + ')';
|
||||
lightboxEl.classList.toggle('is-zoomed', zoom > 1);
|
||||
}
|
||||
|
||||
function toggleZoom() {
|
||||
if (!lightboxFlag('imgbox_zoom', true)) return;
|
||||
var maxZoom = window.innerWidth < 768 ? MAX_ZOOM_MOBILE : MAX_ZOOM_DESKTOP;
|
||||
setZoom(currentZoom > 1 ? 1 : maxZoom);
|
||||
}
|
||||
|
||||
function toggleAutoplay() {
|
||||
if (!lightboxEl) return;
|
||||
var icon = lightboxEl.querySelector('.toggle-play i');
|
||||
if (isAutoplay) {
|
||||
clearInterval(autoplayTimer);
|
||||
autoplayTimer = null;
|
||||
isAutoplay = false;
|
||||
icon.className = 'fa fas fa-play';
|
||||
} else {
|
||||
if (currentGallery.length < 2) return;
|
||||
isAutoplay = true;
|
||||
icon.className = 'fa fas fa-pause';
|
||||
autoplayTimer = setInterval(function () { navigateLightbox(1); }, 3000);
|
||||
return;
|
||||
}
|
||||
if (currentGallery.length < 2) return;
|
||||
|
||||
isAutoplay = true;
|
||||
icon.className = 'fa fas fa-pause';
|
||||
autoplayTimer = setInterval(function () { navigateLightbox(1); }, 3000);
|
||||
}
|
||||
|
||||
function toggleFullscreen() {
|
||||
var rfs = lightboxEl.requestFullscreen || lightboxEl.webkitRequestFullscreen || lightboxEl.msRequestFullscreen;
|
||||
if (document.fullscreenElement || document.webkitFullscreenElement) {
|
||||
(document.exitFullscreen || document.webkitExitFullscreen || document.msExitFullscreen).call(document);
|
||||
} else if (rfs) {
|
||||
rfs.call(lightboxEl);
|
||||
if (!lightboxEl || !lightboxFlag('imgbox_full', true)) return;
|
||||
var fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement;
|
||||
if (fullscreenElement) {
|
||||
var exit = document.exitFullscreen || document.webkitExitFullscreen;
|
||||
if (exit) exit.call(document);
|
||||
return;
|
||||
}
|
||||
|
||||
var request = lightboxEl.requestFullscreen || lightboxEl.webkitRequestFullscreen;
|
||||
if (request) request.call(lightboxEl);
|
||||
}
|
||||
|
||||
function syncFullscreenState() {
|
||||
if (!lightboxEl) return;
|
||||
var active = Boolean(document.fullscreenElement || document.webkitFullscreenElement);
|
||||
var icon = lightboxEl.querySelector('.toggle-full i');
|
||||
lightboxEl.classList.toggle('full-screen', active);
|
||||
icon.className = active ? 'fa fas fa-compress' : 'fa fas fa-expand';
|
||||
}
|
||||
|
||||
function buildThumbnails() {
|
||||
if (!lightboxThumbsWrap) return;
|
||||
lightboxThumbsWrap.innerHTML = '';
|
||||
currentGallery.forEach(function (item, i) {
|
||||
currentGallery.forEach(function (item, index) {
|
||||
var button = document.createElement('button');
|
||||
var thumb = document.createElement('img');
|
||||
thumb.src = item.src;
|
||||
thumb.className = 'aksrt-lightbox__thumb' + (i === currentIndex ? ' is-active' : '');
|
||||
thumb.addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
showLightboxImage(i);
|
||||
button.type = 'button';
|
||||
button.className = 'aksrt-lightbox__thumb' + (index === currentIndex ? ' is-active' : '');
|
||||
button.setAttribute('aria-label', '查看第 ' + (index + 1) + ' 张图片');
|
||||
thumb.src = item.thumbSrc || item.src;
|
||||
thumb.alt = '';
|
||||
button.appendChild(thumb);
|
||||
button.addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
showLightboxImage(index);
|
||||
});
|
||||
lightboxThumbsWrap.appendChild(thumb);
|
||||
lightboxThumbsWrap.appendChild(button);
|
||||
});
|
||||
}
|
||||
|
||||
function syncThumbActive() {
|
||||
if (!lightboxThumbsWrap) return;
|
||||
var thumbs = lightboxThumbsWrap.querySelectorAll('.aksrt-lightbox__thumb');
|
||||
thumbs.forEach(function (t, i) { t.classList.toggle('is-active', i === currentIndex); });
|
||||
thumbs.forEach(function (thumb, index) {
|
||||
thumb.classList.toggle('is-active', index === currentIndex);
|
||||
});
|
||||
var active = thumbs[currentIndex];
|
||||
if (active) active.scrollIntoView({ inline: 'center', block: 'nearest', behavior: 'smooth' });
|
||||
if (active) active.scrollIntoView({ inline: 'center', block: 'nearest' });
|
||||
}
|
||||
|
||||
function syncControls() {
|
||||
if (!lightboxEl) return;
|
||||
var multiple = currentGallery.length > 1;
|
||||
var fullscreenAvailable = Boolean(
|
||||
lightboxEl.requestFullscreen
|
||||
|| lightboxEl.webkitRequestFullscreen
|
||||
|| document.fullscreenEnabled
|
||||
|| document.webkitFullscreenEnabled
|
||||
);
|
||||
var thumbsEnabled = multiple && lightboxFlag('imgbox_thumbs', true);
|
||||
|
||||
lightboxEl.querySelector('.toggle-zoom').hidden = !lightboxFlag('imgbox_zoom', true);
|
||||
lightboxEl.querySelector('.toggle-thumbs').hidden = !thumbsEnabled;
|
||||
lightboxEl.querySelector('.toggle-play').hidden = !multiple || !lightboxFlag('imgbox_play', false);
|
||||
lightboxEl.querySelector('.toggle-full').hidden = !lightboxFlag('imgbox_full', true) || !fullscreenAvailable;
|
||||
lightboxEl.querySelector('.aksrt-lightbox__download').hidden = !lightboxFlag('imgbox_down', false);
|
||||
lightboxEl.querySelector('.aksrt-lightbox__nav--prev').hidden = !multiple;
|
||||
lightboxEl.querySelector('.aksrt-lightbox__nav--next').hidden = !multiple;
|
||||
lightboxThumbsWrap.hidden = !thumbsEnabled;
|
||||
}
|
||||
|
||||
function openLightbox(src, caption, gallery, index, originEl) {
|
||||
createLightbox();
|
||||
if (!lightboxEnabled() || !createLightbox()) return false;
|
||||
|
||||
currentGallery = gallery || [{ src: src, caption: caption || '' }];
|
||||
currentIndex = index || 0;
|
||||
currentGallery = gallery && gallery.length ? gallery : [{ src: src, fullSrc: src, thumbSrc: src, caption: caption || '' }];
|
||||
currentIndex = Number.isInteger(index) ? index : 0;
|
||||
currentZoom = 1;
|
||||
lastTrigger = originEl || document.activeElement;
|
||||
|
||||
// Origin animation
|
||||
if (originEl) {
|
||||
if (originEl && typeof originEl.getBoundingClientRect === 'function') {
|
||||
var rect = originEl.getBoundingClientRect();
|
||||
lightboxEl.style.setProperty('--imgbox-origin-top', (rect.top + rect.height / 2) + 'px');
|
||||
lightboxEl.style.setProperty('--imgbox-origin-left', (rect.left + rect.width / 2) + 'px');
|
||||
@@ -314,16 +395,22 @@
|
||||
lightboxEl.classList.remove('from-origin');
|
||||
}
|
||||
|
||||
showLightboxImage(currentIndex);
|
||||
buildThumbnails();
|
||||
syncControls();
|
||||
showLightboxImage(currentIndex);
|
||||
|
||||
lightboxEl.classList.add('is-open');
|
||||
lightboxEl.setAttribute('aria-hidden', 'false');
|
||||
previousBodyOverflow = document.body.style.overflow;
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
document.body.classList.add('aksrt-lightbox-open');
|
||||
lightboxEl.querySelector('.aksrt-lightbox__close').focus();
|
||||
lightboxEl.dispatchEvent(new CustomEvent('aksrt:lightbox-open', { bubbles: true }));
|
||||
return true;
|
||||
}
|
||||
|
||||
function showLightboxImage(index) {
|
||||
if (!currentGallery.length || !lightboxImg) return;
|
||||
if (index < 0) index = currentGallery.length - 1;
|
||||
if (index >= currentGallery.length) index = 0;
|
||||
currentIndex = index;
|
||||
@@ -333,88 +420,113 @@
|
||||
lightboxImg.src = item.fullSrc || item.src;
|
||||
lightboxImg.alt = item.caption || '';
|
||||
lightboxCaption.textContent = item.caption || '';
|
||||
lightboxCaption.style.display = item.caption ? '' : 'none';
|
||||
lightboxCaption.hidden = !item.caption;
|
||||
|
||||
if (currentGallery.length > 1) {
|
||||
lightboxCounter.textContent = (currentIndex + 1) + ' / ' + currentGallery.length;
|
||||
lightboxCounter.style.display = '';
|
||||
lightboxCounter.hidden = false;
|
||||
} else {
|
||||
lightboxCounter.style.display = 'none';
|
||||
lightboxCounter.textContent = '';
|
||||
lightboxCounter.hidden = true;
|
||||
}
|
||||
|
||||
var download = lightboxEl.querySelector('.aksrt-lightbox__download');
|
||||
var fullSrc = item.fullSrc || item.src;
|
||||
download.href = fullSrc;
|
||||
download.download = fullSrc.substring(fullSrc.lastIndexOf('/') + 1).split(/[?#]/)[0] || 'image';
|
||||
syncThumbActive();
|
||||
}
|
||||
|
||||
function navigateLightbox(dir) {
|
||||
showLightboxImage(currentIndex + dir);
|
||||
function navigateLightbox(direction) {
|
||||
if (currentGallery.length < 2) return;
|
||||
showLightboxImage(currentIndex + direction);
|
||||
}
|
||||
|
||||
function closeLightbox() {
|
||||
if (!lightboxEl) return;
|
||||
if (!lightboxEl || !lightboxEl.classList.contains('is-open')) return;
|
||||
if (isAutoplay) toggleAutoplay();
|
||||
lightboxEl.classList.remove('is-open', 'is-zoomed', 'from-origin');
|
||||
document.body.style.overflow = '';
|
||||
lightboxEl.setAttribute('aria-hidden', 'true');
|
||||
document.body.style.overflow = previousBodyOverflow;
|
||||
document.body.classList.remove('aksrt-lightbox-open');
|
||||
lightboxThumbsWrap.hidden = true;
|
||||
if (document.fullscreenElement) {
|
||||
(document.exitFullscreen || document.webkitExitFullscreen || document.msExitFullscreen).call(document);
|
||||
lightboxImg.src = '';
|
||||
lightboxImg.alt = '';
|
||||
|
||||
var fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement;
|
||||
var exit = document.exitFullscreen || document.webkitExitFullscreen;
|
||||
if (fullscreenElement && exit) exit.call(document);
|
||||
|
||||
if (lastTrigger && typeof lastTrigger.focus === 'function' && document.contains(lastTrigger)) {
|
||||
lastTrigger.focus({ preventScroll: true });
|
||||
}
|
||||
lastTrigger = null;
|
||||
}
|
||||
|
||||
function buildGallery(img) {
|
||||
var container = img.closest(GROUP_SEL);
|
||||
if (!container || container.closest('[data-no-lightbox],[data-aksrt-lightbox="0"]')) return null;
|
||||
|
||||
var gallery = [];
|
||||
var foundIndex = -1;
|
||||
container.querySelectorAll('img').forEach(function (candidate) {
|
||||
if (!candidate.matches(TRIGGER_SEL) || isNoLightbox(candidate)) return;
|
||||
var fullSrc = resolveFullUrl(candidate);
|
||||
var thumbSrc = resolveThumbUrl(candidate) || fullSrc;
|
||||
if (!fullSrc) return;
|
||||
|
||||
if (candidate === img) foundIndex = gallery.length;
|
||||
gallery.push({
|
||||
src: thumbSrc,
|
||||
thumbSrc: thumbSrc,
|
||||
fullSrc: fullSrc,
|
||||
caption: resolveCaption(candidate),
|
||||
});
|
||||
});
|
||||
|
||||
if (foundIndex < 0 || !gallery.length) return null;
|
||||
return { items: gallery, index: foundIndex };
|
||||
}
|
||||
|
||||
function openImage(img) {
|
||||
if (isNoLightbox(img)) return false;
|
||||
var gallery = buildGallery(img);
|
||||
if (!gallery) return false;
|
||||
var item = gallery.items[gallery.index];
|
||||
return openLightbox(item.fullSrc, item.caption, gallery.items, gallery.index, img);
|
||||
}
|
||||
|
||||
// Auto-bind: expanded trigger scope + exclusion rules
|
||||
function initLightbox() {
|
||||
document.addEventListener('click', function (e) {
|
||||
// Also support a[data-imgbox]
|
||||
var anchor = e.target.closest('a[data-imgbox]');
|
||||
if (anchor) {
|
||||
e.preventDefault();
|
||||
var href = anchor.getAttribute('href') || anchor.getAttribute('data-imgbox');
|
||||
openLightbox(href, '', [{ src: href, fullSrc: href, caption: '' }], 0, anchor);
|
||||
document.addEventListener('click', function (event) {
|
||||
if (!lightboxEnabled() || !(event.target instanceof Element)) return;
|
||||
|
||||
var anchor = event.target.closest(ANCHOR_TRIGGER_SEL);
|
||||
if (anchor && !anchor.closest(EXCLUDE_SEL)) {
|
||||
var anchorImage = anchor.querySelector('img');
|
||||
if (anchorImage && openImage(anchorImage)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
var href = anchor.getAttribute('href') || '';
|
||||
if (IMAGE_URL_RE.test(href) && openLightbox(href, anchor.getAttribute('data-caption') || '', null, 0, anchor)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var img = e.target.closest(TRIGGER_SEL);
|
||||
if (!img) return;
|
||||
if (img.width < 100 || img.height < 100) return;
|
||||
if (!lightboxEnabled() || isNoLightbox(img)) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
var container = img.closest(CONTAINER_SEL);
|
||||
if (!container) return;
|
||||
|
||||
var allImages = container.querySelectorAll('img');
|
||||
var gallery = [];
|
||||
var foundIndex = 0;
|
||||
|
||||
allImages.forEach(function (el) {
|
||||
if (el.width < 100 || el.height < 100) return;
|
||||
if (isNoLightbox(el)) return;
|
||||
var full = resolveFullUrl(el);
|
||||
var caption = el.alt || '';
|
||||
var figcaption = el.closest('figure') ? el.closest('figure').querySelector('figcaption') : null;
|
||||
if (figcaption) caption = figcaption.textContent;
|
||||
gallery.push({ src: el.dataset.src || el.src, fullSrc: full, caption: caption });
|
||||
if (el === img) foundIndex = gallery.length - 1;
|
||||
});
|
||||
|
||||
if (gallery.length > 0) {
|
||||
openLightbox(img.dataset.src || img.src, img.alt, gallery, foundIndex, img);
|
||||
}
|
||||
var img = event.target.closest(TRIGGER_SEL);
|
||||
if (img && openImage(img)) event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
// 暴露到全局
|
||||
window.AksrtLightbox = {
|
||||
open: openLightbox,
|
||||
close: closeLightbox,
|
||||
};
|
||||
|
||||
/* ============================================================
|
||||
* 2. 滚动方向感知
|
||||
* ============================================================ */
|
||||
// Scroll direction is now handled by aksrt-runtime.js body scroll bus
|
||||
// (body.scroll-down / body.scroll-up / body.body-scroll).
|
||||
// This stub syncs the legacy aksrt-* prefixed classes for backward compat.
|
||||
// Scroll direction is handled by aksrt-runtime.js. Keep the prefixed
|
||||
// classes in sync for the remaining CSS consumers.
|
||||
function initScrollDirection() {
|
||||
var body = document.body;
|
||||
|
||||
@@ -430,26 +542,13 @@
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function onScroll() {
|
||||
window.addEventListener('scroll', function () {
|
||||
requestAnimationFrame(sync);
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 3. 导航溢出折叠
|
||||
* 由 legacy main.js 的 navFolding 负责。
|
||||
* 此处不再重复实现,避免两套逻辑争抢 .aksrt-nav-more 节点
|
||||
* 触发 NotFoundError: insertBefore (moreLi 已被对方 remove)。
|
||||
* ============================================================ */
|
||||
|
||||
/* ============================================================
|
||||
* 初始化入口
|
||||
* ============================================================ */
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
initLightbox();
|
||||
initScrollDirection();
|
||||
});
|
||||
|
||||
})();
|
||||
Reference in new issue
Block a user