feat: 文章正文图片支持灯箱;删除未使用的 GitHub Card 插件
灯箱 - 相册页原本已有一套无依赖灯箱(事件委托 + ClientRouter 友好), 但它写在 gallery/[album].astro 里,只有相册页能用。 现抽成 src/components/ImageLightbox.astro,挂到 BaseLayout,全站可用。 - 新增 src/plugins/rehype-image-lightbox.mjs:给正文里的 <img> 注入 data-lightbox-src。跳过链接内的图片(点击应跳转)和显式标记 data-lightbox-ignore 的图片。 - 顺带让灯箱大图继承原图的 alt 文本(原先固定为空)。 - 正文图片加 cursor: zoom-in 提示。 清理 - 删除 src/plugins/rehype-component-github-card.mjs(165 行)及其在 rehypeComponents 里的 github 映射:内容里从未使用过 ::github 指令。 验证:构建 41 页通过;文章页正文图片 4/4 带上 data-lightbox-src, 相册页 5 个触发元素不受影响;灯箱脚本按页内联(2.8KB 压缩后); 正文渲染文本与线上基线一致,无回归。
This commit is contained in:
@@ -122,115 +122,3 @@ const description = album.description ?? "相册"
|
||||
break-inside: avoid;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 无依赖灯箱。
|
||||
* 用事件委托 + 每次打开时重新收集当前页的照片,因此 ClientRouter 切页后依然有效。
|
||||
*/
|
||||
const LB_ID = "vv-lightbox"
|
||||
|
||||
const ensureOverlay = (): HTMLElement => {
|
||||
let overlay = document.getElementById(LB_ID)
|
||||
if (overlay) return overlay
|
||||
|
||||
overlay = document.createElement("div")
|
||||
overlay.id = LB_ID
|
||||
overlay.setAttribute("role", "dialog")
|
||||
overlay.setAttribute("aria-modal", "true")
|
||||
overlay.className =
|
||||
"fixed inset-0 z-[100] hidden items-center justify-center bg-black/90 backdrop-blur-sm"
|
||||
overlay.innerHTML = `
|
||||
<button type="button" data-lb-close aria-label="关闭"
|
||||
class="absolute right-4 top-4 z-10 flex h-10 w-10 items-center justify-center rounded-full bg-white/10 text-2xl leading-none text-white/90 transition-colors hover:bg-white/20">×</button>
|
||||
<button type="button" data-lb-prev aria-label="上一张"
|
||||
class="absolute left-3 z-10 flex h-11 w-11 items-center justify-center rounded-full bg-white/10 text-2xl leading-none text-white/90 transition-colors hover:bg-white/20 sm:left-6">‹</button>
|
||||
<button type="button" data-lb-next aria-label="下一张"
|
||||
class="absolute right-3 z-10 flex h-11 w-11 items-center justify-center rounded-full bg-white/10 text-2xl leading-none text-white/90 transition-colors hover:bg-white/20 sm:right-6">›</button>
|
||||
<img data-lb-img src="" alt="" class="max-h-[88vh] max-w-[92vw] object-contain" />
|
||||
<div data-lb-count
|
||||
class="absolute bottom-5 left-1/2 -translate-x-1/2 rounded-full bg-white/10 px-3 py-1 font-mono text-xs text-white/80"></div>
|
||||
`
|
||||
document.body.appendChild(overlay)
|
||||
return overlay
|
||||
}
|
||||
|
||||
let sources: string[] = []
|
||||
let current = 0
|
||||
|
||||
const render = (overlay: HTMLElement) => {
|
||||
const img = overlay.querySelector<HTMLImageElement>("[data-lb-img]")
|
||||
const counter = overlay.querySelector<HTMLElement>("[data-lb-count]")
|
||||
if (img) img.src = sources[current] ?? ""
|
||||
if (counter) counter.textContent = `${current + 1} / ${sources.length}`
|
||||
const single = sources.length < 2
|
||||
for (const sel of ["[data-lb-prev]", "[data-lb-next]"]) {
|
||||
const el = overlay.querySelector<HTMLElement>(sel)
|
||||
if (el) el.style.display = single ? "none" : ""
|
||||
}
|
||||
}
|
||||
|
||||
const openAt = (index: number) => {
|
||||
const overlay = ensureOverlay()
|
||||
current = index
|
||||
render(overlay)
|
||||
overlay.classList.remove("hidden")
|
||||
overlay.classList.add("flex")
|
||||
document.body.style.overflow = "hidden"
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
const overlay = document.getElementById(LB_ID)
|
||||
if (!overlay) return
|
||||
overlay.classList.add("hidden")
|
||||
overlay.classList.remove("flex")
|
||||
document.body.style.overflow = ""
|
||||
}
|
||||
|
||||
const step = (delta: number) => {
|
||||
if (sources.length < 2) return
|
||||
current = (current + delta + sources.length) % sources.length
|
||||
const overlay = document.getElementById(LB_ID)
|
||||
if (overlay) render(overlay)
|
||||
}
|
||||
|
||||
if (!(window as unknown as { __vvLightboxBound?: boolean }).__vvLightboxBound) {
|
||||
;(window as unknown as { __vvLightboxBound?: boolean }).__vvLightboxBound = true
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
const target = event.target as Element | null
|
||||
|
||||
const trigger = target?.closest<HTMLElement>("[data-lightbox-src]")
|
||||
if (trigger) {
|
||||
// 每次打开都重新收集当前页的照片
|
||||
const all = Array.from(document.querySelectorAll<HTMLElement>("[data-lightbox-src]"))
|
||||
sources = all.map((el) => el.dataset.lightboxSrc ?? "")
|
||||
openAt(Number(trigger.dataset.lightboxIndex ?? all.indexOf(trigger)))
|
||||
return
|
||||
}
|
||||
|
||||
if (target?.closest("[data-lb-close]")) return close()
|
||||
if (target?.closest("[data-lb-prev]")) return step(-1)
|
||||
if (target?.closest("[data-lb-next]")) return step(1)
|
||||
|
||||
// 点背景关闭(点图片本身不关)
|
||||
const overlay = document.getElementById(LB_ID)
|
||||
if (overlay && target === overlay) close()
|
||||
})
|
||||
|
||||
document.addEventListener("keydown", (event) => {
|
||||
const overlay = document.getElementById(LB_ID)
|
||||
if (!overlay || overlay.classList.contains("hidden")) return
|
||||
if (event.key === "Escape") close()
|
||||
if (event.key === "ArrowLeft") step(-1)
|
||||
if (event.key === "ArrowRight") step(1)
|
||||
})
|
||||
|
||||
// 切页时收拾干净,避免残留遮罩锁住滚动
|
||||
document.addEventListener("astro:page-load", () => {
|
||||
const overlay = document.getElementById(LB_ID)
|
||||
if (overlay) overlay.remove()
|
||||
document.body.style.overflow = ""
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user