diff --git a/frontend/src/components/ImgLightbox.vue b/frontend/src/components/ImgLightbox.vue index 7c34c3f..a1b9a82 100644 --- a/frontend/src/components/ImgLightbox.vue +++ b/frontend/src/components/ImgLightbox.vue @@ -2,41 +2,70 @@ import { onBeforeUnmount, watch } from 'vue' import { lightboxState as st, closeLightbox, stepLightbox } from '../lightbox' -// 图片预览层:App.vue 全局挂一次。铺满全屏的暗底 + 居中大图, -// 多图时左右箭头 / 方向键切换,角标显示第几张;Esc / 点空白收起。 +// 图片预览层(手机重做版):App.vue 全局挂一次。 +// +// 手机上出过两次事故,这次的实现原则: +// 1. 不碰 body 的 overflow——之前用 inline overflow 锁背景,把样式表的 +// overflow-x: clip 覆盖成 hidden,横向从「裁剪」变「可滚动容器」, +// 整页被拖宽。现在滚动封锁靠遮罩自己的 @wheel/@touchmove.prevent: +// 事件不落到页面里,背景自然滚不动,滚动位置也天然保留。 +// 2. 图片尺寸全部用百分比(max-width/height: 100%),不引入 vw/vh/dvh—— +// 这些单位在全面屏 URL 栏收展时各有各的坑,遮罩本身就是视口大小。 +// 3. 手势全归遮罩:touch-action: none + touchend 位移判滑动翻页, +// 双击缩放/长按菜单/下拉刷新这些浏览器手势都不会来捣乱。 +// 4. 桌面能力不变:Esc / 方向键 / 点空白关闭 / 悬停箭头。 function onKey(e) { if (e.key === 'Escape') closeLightbox() if (e.key === 'ArrowLeft') stepLightbox(-1) if (e.key === 'ArrowRight') stepLightbox(1) } + +// 吸顶栏带 backdrop-filter,真机上其合成层会盖在遮罩之上 +// (z-index 更高也拦不住)——开预览时整个藏掉 watch( () => st.open, (v) => { - if (v) { - window.addEventListener('keydown', onKey) - // 吸顶栏带 backdrop-filter,真机上其合成层会盖在遮罩之上 - // (遮罩 z-index 更高也拦不住)——开预览时整个藏掉 - document.body.classList.add('lb-open') - } else { - window.removeEventListener('keydown', onKey) - document.body.classList.remove('lb-open') - } + if (v) window.addEventListener('keydown', onKey) + else window.removeEventListener('keydown', onKey) + document.body.classList.toggle('lb-open', v) } ) -onBeforeUnmount(() => window.removeEventListener('keydown', onKey)) +onBeforeUnmount(() => { + window.removeEventListener('keydown', onKey) + 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) { + if (st.list.length < 2) return + 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) { + stepLightbox(dx < 0 ? 1 : -1) + } +}