撤销 ce07f4a(lightbox 手机适配 + 正文图预览委托):真机上反复出问题(撑屏/缩放错乱),全部回退到原有简单预览;仅保留 lb-open 隐藏吸顶栏的亮带修复

This commit is contained in:
Sakurasan
2026-09-29 02:07:06 +08:00
parent c6eb0bbe5b
commit 84f88cb473
3 changed files with 16 additions and 85 deletions
+2 -25
View File
@@ -7,26 +7,9 @@ import ImgLightbox from './components/ImgLightbox.vue'
import YohakuTop from './ui/yohaku/YohakuTop.vue' import YohakuTop from './ui/yohaku/YohakuTop.vue'
import YohakuFoot from './ui/yohaku/YohakuFoot.vue' import YohakuFoot from './ui/yohaku/YohakuFoot.vue'
import { site, setAdminScope } from './site' import { site, setAdminScope } from './site'
import { openLightbox } from './lightbox'
const route = useRoute() const route = useRoute()
// 正文图片点击预览(capture 阶段):时间线/详情页里 v-html 的正文图
// 都没有自己的点击入口,这里统一接管。capture + stopPropagation 是为了
// 压住整卡点击(时间线点图不该跳详情);配图网格有自己的 handler 不在
// .prose 内,天然不受影响。lightbox 用 data-fullsrc 原图(thumbify 保留)。
function onProseImgClick(e) {
const img = e.target
if (!img || img.tagName !== 'IMG') return
const scope = img.closest('.prose')
if (!scope || img.closest('.post-imgs') || img.closest('a')) return
const list = [...scope.querySelectorAll('img')].map((i) => i.dataset.fullsrc || i.currentSrc || i.src)
const cur = img.dataset.fullsrc || img.currentSrc || img.src
const idx = Math.max(0, list.indexOf(cur))
e.stopPropagation()
openLightbox(list, idx)
}
// 正文代码块复制按钮(事件委托:复制按钮是 enhanceProse 注入的静态 HTML) // 正文代码块复制按钮(事件委托:复制按钮是 enhanceProse 注入的静态 HTML)
function onCopyClick(e) { function onCopyClick(e) {
const btn = e.target.closest?.('.pre-copy') const btn = e.target.closest?.('.pre-copy')
@@ -48,14 +31,8 @@ function onCopyClick(e) {
setTimeout(() => (btn.textContent = '复制'), 1600) setTimeout(() => (btn.textContent = '复制'), 1600)
}) })
} }
onMounted(() => { onMounted(() => document.addEventListener('click', onCopyClick))
document.addEventListener('click', onCopyClick) onBeforeUnmount(() => document.removeEventListener('click', onCopyClick))
document.addEventListener('click', onProseImgClick, true)
})
onBeforeUnmount(() => {
document.removeEventListener('click', onCopyClick)
document.removeEventListener('click', onProseImgClick, true)
})
const isAdmin = computed(() => route.path.startsWith('/admin')) const isAdmin = computed(() => route.path.startsWith('/admin'))
// vivid 只在公开前台生效:后台永远走 classic 那套(--admin-* token + 原 data-ui/data-theme), // vivid 只在公开前台生效:后台永远走 classic 那套(--admin-* token + 原 data-ui/data-theme),
+13 -53
View File
@@ -3,59 +3,34 @@ import { onBeforeUnmount, watch } from 'vue'
import { lightboxState as st, closeLightbox, stepLightbox } from '../lightbox' import { lightboxState as st, closeLightbox, stepLightbox } from '../lightbox'
// 图片预览层:App.vue 全局挂一次。铺满全屏的暗底 + 居中大图, // 图片预览层:App.vue 全局挂一次。铺满全屏的暗底 + 居中大图,
// 多图时左右箭头 / 方向键 / 手机滑动切换,角标显示第几张;Esc / 点空白收起。 // 多图时左右箭头 / 方向键切换,角标显示第几张;Esc / 点空白收起。
function onKey(e) { function onKey(e) {
if (e.key === 'Escape') closeLightbox() if (e.key === 'Escape') closeLightbox()
if (e.key === 'ArrowLeft') stepLightbox(-1) if (e.key === 'ArrowLeft') stepLightbox(-1)
if (e.key === 'ArrowRight') stepLightbox(1) if (e.key === 'ArrowRight') stepLightbox(1)
} }
// 打开时锁住背景滚动(手机上尤其明显:否则手指滑动会带着页面一起动)
let prevOverflow = ''
watch( watch(
() => st.open, () => st.open,
(v) => { (v) => {
if (v) { if (v) {
window.addEventListener('keydown', onKey) window.addEventListener('keydown', onKey)
prevOverflow = document.body.style.overflowY // 吸顶栏带 backdrop-filter,真机上其合成层会盖在遮罩之上
// 只锁纵向:整个 overflow 改 hidden 会覆盖样式表的 overflow-x: clip, // (遮罩 z-index 更高也拦不住)——开预览时整个藏掉
// 横向从「裁剪」变「可滚动容器」,手机上整页能被拖宽(真实事故)
document.body.style.overflowY = 'hidden'
document.body.classList.add('lb-open') document.body.classList.add('lb-open')
} else { } else {
window.removeEventListener('keydown', onKey) window.removeEventListener('keydown', onKey)
document.body.style.overflowY = prevOverflow
document.body.classList.remove('lb-open') document.body.classList.remove('lb-open')
} }
} }
) )
onBeforeUnmount(() => { onBeforeUnmount(() => window.removeEventListener('keydown', onKey))
window.removeEventListener('keydown', onKey)
document.body.style.overflowY = prevOverflow
document.body.classList.remove('lb-open')
})
// 手机滑动翻页:横向位移超 44px 且大于纵向位移才算一次滑动
let touchX = 0
let touchY = 0
function onTouchStart(e) {
touchX = e.touches[0].clientX
touchY = e.touches[0].clientY
}
function onTouchEnd(e) {
const dx = e.changedTouches[0].clientX - touchX
const dy = e.changedTouches[0].clientY - touchY
if (Math.abs(dx) > 44 && Math.abs(dx) > Math.abs(dy) * 1.4 && st.list.length > 1) {
stepLightbox(dx < 0 ? 1 : -1)
}
}
</script> </script>
<template> <template>
<Teleport to="body"> <Teleport to="body">
<Transition name="lb"> <Transition name="lb">
<div v-if="st.open" class="lb-overlay" @click="closeLightbox" @touchstart.passive="onTouchStart" @touchend.passive="onTouchEnd"> <div v-if="st.open" class="lb-overlay" @click="closeLightbox">
<img <img
class="lb-img" class="lb-img"
:src="st.list[st.index]" :src="st.list[st.index]"
@@ -156,43 +131,28 @@ function onTouchEnd(e) {
} }
@media (max-width: 640px) { @media (max-width: 640px) {
.lb-overlay {
/* 全面屏 URL 栏收展时 100vh 会偏大,dvh 跟随可视高度 */
align-items: center;
}
.lb-img { .lb-img {
max-width: 100vw; max-width: 96vw;
max-height: 74dvh; max-height: 74vh;
border-radius: 0;
} }
.lb-nav { .lb-nav {
width: 40px; width: 38px;
height: 40px; height: 38px;
font-size: 22px; font-size: 22px;
/* 悬浮在图片两侧半透明,可见但不抢戏 */
background: rgba(255, 255, 255, 0.12);
} }
.lb-nav.prev { .lb-nav.prev {
left: calc(8px + env(safe-area-inset-left)); left: 10px;
} }
.lb-nav.next { .lb-nav.next {
right: calc(8px + env(safe-area-inset-right)); right: 10px;
} }
.lb-close { .lb-close {
top: calc(10px + env(safe-area-inset-top)); top: 10px;
right: calc(10px + env(safe-area-inset-right)); right: 10px;
width: 42px;
height: 42px;
}
.lb-count {
bottom: calc(14px + env(safe-area-inset-bottom));
font-size: 12px;
} }
} }
+1 -7
View File
@@ -70,13 +70,7 @@ export function applyImageRatio(html) {
export function thumbifyHtml(html, w = 480) { export function thumbifyHtml(html, w = 480) {
if (!html) return html if (!html) return html
return html.replace(/<img\b[^>]*?>/gi, (tag) => { return html.replace(/<img\b[^>]*?>/gi, (tag) => {
let out = tag let out = tag.replace(/\bsrc="([^"]*)"/i, (m, src) => 'src="' + thumbURL(src, w) + '"')
// 原图地址留在 data-fullsrc:lightbox 打开时用原图,不用 480w 缩略图
out = out.replace(/\bsrc="([^"]*)"/i, (m, src) => {
const thumb = thumbURL(src, w)
if (thumb !== src) out = out.replace(/^(<img)/i, '$1 data-fullsrc="' + src + '"')
return 'src="' + thumb + '"'
})
if (!/\bloading=/i.test(out)) out = out.replace(/^<img/i, '<img loading="lazy"') if (!/\bloading=/i.test(out)) out = out.replace(/^<img/i, '<img loading="lazy"')
return out return out
}) })