Files
TripLog/templates/_lightbox.php
T

126 lines
4.6 KiB
PHP
Raw Normal View History

<?php
/**
* 照片灯箱(Lightbox):点页面里带 class="js-photo" 的链接,就在当前页弹窗里看大图,
* 左右箭头(或键盘 ←/→、触摸滑动)切上一张/下一张,不再新开窗口。
*
* 用法:
* 1. 图片链接写成 <a class="js-photo" href="<大图地址>" data-cap="<说明文字>">…</a>
* (没有 data-cap 时回落到 title / alt / href)
* 2. 在页面末尾 require 本模板(一份就够,多页共用同一份标记与脚本)
*
* 依赖外部变量:无。
*/
?>
<div class="lightbox" id="lightbox" aria-hidden="true" role="dialog" aria-label="图片浏览">
<div class="lightbox-backdrop" data-lb-close="1"></div>
<button type="button" class="lightbox-nav lightbox-prev" data-lb-prev="1" aria-label="上一张">&#8249;</button>
<figure class="lightbox-body">
<img class="lightbox-img" id="lightbox-img" alt="">
<figcaption class="lightbox-cap" id="lightbox-cap"></figcaption>
</figure>
<button type="button" class="lightbox-nav lightbox-next" data-lb-next="1" aria-label="下一张">&#8250;</button>
<button type="button" class="lightbox-close" data-lb-close="1" aria-label="关闭">×</button>
<div class="lightbox-count" id="lightbox-count"></div>
</div>
<script>
(function () {
'use strict';
var box = document.getElementById('lightbox');
if (!box) return;
var img = document.getElementById('lightbox-img');
var cap = document.getElementById('lightbox-cap');
var count = document.getElementById('lightbox-count');
var items = []; // 打开时现收集页面上所有 js-photo(DOM 顺序)
var idx = 0;
function collect() {
return Array.prototype.slice.call(document.querySelectorAll('a.js-photo'));
}
function captionOf(a) {
return a.getAttribute('data-cap') || a.getAttribute('title')
|| (a.querySelector('img') ? a.querySelector('img').alt : '') || '';
}
function show(i) {
if (!items.length) return;
idx = (i + items.length) % items.length; // 循环切换
var a = items[idx];
var full = a.getAttribute('data-full') || a.getAttribute('href');
img.alt = captionOf(a);
cap.textContent = captionOf(a);
count.textContent = items.length > 1 ? (idx + 1) + ' / ' + items.length : '';
img.src = full;
// 预加载相邻两张,切换不等待
[idx - 1, idx + 1].forEach(function (j) {
var na = items[(j + items.length) % items.length];
if (!na) return;
var pre = new Image();
pre.src = na.getAttribute('data-full') || na.getAttribute('href');
});
}
function openAt(anchor) {
items = collect();
var at = items.indexOf(anchor);
if (at < 0) { items = [anchor]; at = 0; }
box.classList.add('open');
box.setAttribute('aria-hidden', 'false');
document.body.style.overflow = 'hidden';
show(at);
}
function close() {
box.classList.remove('open');
box.setAttribute('aria-hidden', 'true');
document.body.style.overflow = '';
img.src = '';
}
document.addEventListener('click', function (ev) {
var link = ev.target.closest ? ev.target.closest('a.js-photo') : null;
if (link) {
ev.preventDefault();
openAt(link);
return;
}
if (!box.classList.contains('open')) return;
if (ev.target.closest('[data-lb-prev]')) { show(idx - 1); return; }
if (ev.target.closest('[data-lb-next]')) { show(idx + 1); return; }
if (ev.target.closest('[data-lb-close]')) { close(); }
});
document.addEventListener('keydown', function (ev) {
if (!box.classList.contains('open')) return;
if (ev.key === 'Escape') close();
else if (ev.key === 'ArrowLeft') show(idx - 1);
else if (ev.key === 'ArrowRight') show(idx + 1);
});
// 触摸滑动切图(手机上不用去够箭头)
var touchX = null, touchY = null;
box.addEventListener('touchstart', function (ev) {
if (ev.touches.length === 1) {
touchX = ev.touches[0].clientX;
touchY = ev.touches[0].clientY;
}
}, { passive: true });
box.addEventListener('touchend', function (ev) {
if (touchX === null || !ev.changedTouches.length) return;
var dx = ev.changedTouches[0].clientX - touchX;
var dy = ev.changedTouches[0].clientY - touchY;
if (Math.abs(dx) > 48 && Math.abs(dx) > Math.abs(dy) * 1.5) {
show(dx < 0 ? idx + 1 : idx - 1);
}
touchX = touchY = null;
}, { passive: true });
})();
</script>